package incheon.cmm.g2f.layer.mapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import incheon.com.cmm.context.RequestContext;

/**
 * G2F SqlProvider 클래스 (완전 동적 SQL 생성)
 * 다양한 테이블 구조에 대응할 수 있는 동적 SQL 생성
 * 감사 컬럼(frst_reg_id, frst_reg_dt, last_mdfcn_id, last_mdfcn_dt) 자동 처리
 */
@Component
public class G2FSqlProvider {
    
    // SqlProvider는 정적 메서드로 동작하므로 의존성 주입 불가
    // 대신 서비스 계층에서 메타데이터를 조회해서 파라미터로 전달받음
    
    /**
     * PostgreSQL 데이터 타입 매핑
     */
	private static final Map<String, String> DATA_TYPE_MAPPING = Map.ofEntries(
		    Map.entry("character varying", "varchar"),
		    Map.entry("text", "varchar"),
		    Map.entry("integer", "integer"),
		    Map.entry("bigint", "bigint"),
		    Map.entry("smallint", "smallint"),
		    Map.entry("numeric", "numeric"),
		    Map.entry("double precision", "double precision"),
		    Map.entry("real", "real"),
		    Map.entry("boolean", "boolean"),
		    Map.entry("timestamp without time zone", "timestamp"),
		    Map.entry("timestamp with time zone", "timestamp"),
		    Map.entry("date", "date")
	);
    
    /**
     * 청크 배치 INSERT SQL 생성 (VALUES 여러 행)
     */
    public String batchInsertLayerFeatures(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");
        
        // 테이블 메타데이터는 서비스 계층에서 조회해서 파라미터로 전달받음
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> columns = (List<Map<String, Object>>) params.get("columns");
        
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> features = (List<Map<String, Object>>) params.get("features");
        
        if (features == null || features.isEmpty()) {
            return "SELECT 1 WHERE 1=0"; // 빈 결과를 위한 더미 쿼리
        }
        
        // 공통 컬럼 추출 (첫 번째 피처의 properties 기준)
        @SuppressWarnings("unchecked")
        Map<String, Object> firstProperties = (Map<String, Object>) features.get(0).get("properties");
        List<String> validColumns = new ArrayList<>();

        // 감사 컬럼 체크 (INSERT 시 4개 전부 체크)
        boolean hasFrstRegId = false;
        boolean hasFrstRegDt = false;
        boolean hasLastMdfcnId = false;
        boolean hasLastMdfcnDt = false;

        if (firstProperties != null) {
            for (Map<String, Object> column : columns) {
                String columnName = (String) column.get("column_name");

                // 감사 컬럼 체크 (INSERT 시 4개 전부)
                if ("frst_reg_id".equalsIgnoreCase(columnName)) {
                    hasFrstRegId = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("frst_reg_dt".equalsIgnoreCase(columnName)) {
                    hasFrstRegDt = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("last_mdfcn_id".equalsIgnoreCase(columnName)) {
                    hasLastMdfcnId = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("last_mdfcn_dt".equalsIgnoreCase(columnName)) {
                    hasLastMdfcnDt = true;
                    continue; // validColumns에 추가하지 않음
                }

                if (firstProperties.containsKey(columnName)) {
                    validColumns.add(columnName);
                }
            }
        }

        // 현재 사용자 ID 조회
        String currentUserId = null;
        if (hasFrstRegId || hasLastMdfcnId) {
            currentUserId = RequestContext.getCurrentUserId();
        }
        
        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(schemaName).append(".").append(tableName)
           .append(" (geom");

        for (String columnName : validColumns) {
        	if("idn".equals(columnName) || "ftr_idn".equals(columnName)) continue;	//idn, ftr_idn은 default sequence로 Insert 시에는 제외
            sql.append(", \"").append(columnName).append("\"");
        }

        // 감사 컬럼 추가 (INSERT 시 4개 전부)
        if (hasFrstRegId) {
            sql.append(", frst_reg_id");
        }
        if (hasFrstRegDt) {
            sql.append(", frst_reg_dt");
        }
        if (hasLastMdfcnId) {
            sql.append(", last_mdfcn_id");
        }
        if (hasLastMdfcnDt) {
            sql.append(", last_mdfcn_dt");
        }

        sql.append(") VALUES ");

        // 각 피처에 대한 VALUES 생성
        for (int i = 0; i < features.size(); i++) {
            if (i > 0) sql.append(", ");
            sql.append("(ST_SetSRID(ST_Force2D(ST_GeomFromGeoJSON(#{features[").append(i).append("].geometryString})), #{srid})");

            for (String columnName : validColumns) {
            	if("idn".equals(columnName) || "ftr_idn".equals(columnName)) continue;	//idn, ftr_idn은 default sequence로 Insert 시에는 제외
                String dataType = getColumnDataType(columns, columnName);
                sql.append(", #{features[").append(i).append("].properties.")
                   .append(columnName);

                // PostgreSQL 타입 캐스팅 추가
                if (!dataType.equals("varchar")) {
                    sql.append("}::").append(dataType);
                } else {
                    sql.append("}");
                }
            }

            // 감사 컬럼 값 추가 (INSERT 시 4개 전부 자동 설정)
            if (hasFrstRegId) {
                sql.append(", '").append(currentUserId).append("'");
            }
            if (hasFrstRegDt) {
                sql.append(", CURRENT_TIMESTAMP");
            }
            if (hasLastMdfcnId) {
                sql.append(", '").append(currentUserId).append("'");
            }
            if (hasLastMdfcnDt) {
                sql.append(", CURRENT_TIMESTAMP");
            }

            sql.append(")");
        }

        // RETURNING * 추가하여 저장된 모든 컬럼 정보를 반환
        sql.append(" RETURNING *");

        return sql.toString();
    }
    
    /**
     * 청크 배치 UPDATE SQL 생성 (CASE WHEN 방식)
     */
    public String batchUpdateLayerFeatures(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");
        
        // 테이블 메타데이터는 서비스 계층에서 조회해서 파라미터로 전달받음
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> columns = (List<Map<String, Object>>) params.get("columns");
        
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> features = (List<Map<String, Object>>) params.get("features");
        
        if (features == null || features.isEmpty()) {
            return "UPDATE " + schemaName + "." + tableName + " SET gid = gid WHERE 1=0"; // 더미 쿼리
        }
        
        // 공통 컬럼 추출
        @SuppressWarnings("unchecked")
        Map<String, Object> firstProperties = (Map<String, Object>) features.get(0).get("properties");
        List<String> validColumns = new ArrayList<>();

        // 감사 컬럼 체크
        boolean hasLastMdfcnId = false;
        boolean hasLastMdfcnDt = false;

        if (firstProperties != null) {
            for (Map<String, Object> column : columns) {
                String columnName = (String) column.get("column_name");

                // 감사 컬럼 체크 (UPDATE 시)
                if ("last_mdfcn_id".equalsIgnoreCase(columnName)) {
                    hasLastMdfcnId = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("last_mdfcn_dt".equalsIgnoreCase(columnName)) {
                    hasLastMdfcnDt = true;
                    continue; // validColumns에 추가하지 않음
                }

                if (firstProperties.containsKey(columnName)) {
                    validColumns.add(columnName);
                }
            }
        }

        // 현재 사용자 ID 조회
        String currentUserId = null;
        if (hasLastMdfcnId) {
            currentUserId = RequestContext.getCurrentUserId();
        }
        
        StringBuilder sql = new StringBuilder();
        sql.append("UPDATE ").append(schemaName).append(".").append(tableName).append(" SET ");
        
        // geometry 업데이트 (CASE WHEN 방식)
        sql.append("geom = CASE gid ");
        for (int i = 0; i < features.size(); i++) {
            sql.append("WHEN #{features[").append(i).append("].gid} THEN ST_SetSRID(ST_Force2D(ST_GeomFromGeoJSON(#{features[")
               .append(i).append("].geometryString})), #{srid}) ");
        }
        sql.append("ELSE geom END");
        
        // 각 속성 컬럼 업데이트
        for (String columnName : validColumns) {
            String dataType = getColumnDataType(columns, columnName);
            sql.append(", \"").append(columnName).append("\" = CASE gid ");

            for (int i = 0; i < features.size(); i++) {
                sql.append("WHEN #{features[").append(i).append("].gid} THEN #{features[")
                   .append(i).append("].properties.").append(columnName).append("}");

                // PostgreSQL 타입 캐스팅 추가
                if (!dataType.equals("varchar")) {
                    sql.append("::").append(dataType);
                }

                sql.append(" ");
            }
            sql.append("ELSE \"").append(columnName).append("\" END");
        }

        // 감사 컬럼 업데이트
        if (hasLastMdfcnId) {
            sql.append(", last_mdfcn_id = '").append(currentUserId).append("'");
        }
        if (hasLastMdfcnDt) {
            sql.append(", last_mdfcn_dt = CURRENT_TIMESTAMP");
        }

        // WHERE 조건 (gid IN (...))
        sql.append(" WHERE gid IN (");
        for (int i = 0; i < features.size(); i++) {
            if (i > 0) sql.append(", ");
            sql.append("#{features[").append(i).append("].gid}");
        }
        sql.append(")");
        
        return sql.toString();
    }
    
    /**
     * 동적 DELETE SQL 생성 (배치)
     */
    public String deleteLayerFeatures(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");
        
        @SuppressWarnings("unchecked")
        List<Integer> gids = (List<Integer>) params.get("gids");
        
        if (gids == null || gids.isEmpty()) {
            return "DELETE FROM " + schemaName + "." + tableName + " WHERE 1=0"; // 더미 쿼리
        }
        
        StringBuilder sql = new StringBuilder();
        sql.append("DELETE FROM ").append(schemaName).append(".").append(tableName)
           .append(" WHERE gid IN (");
        
        for (int i = 0; i < gids.size(); i++) {
            if (i > 0) sql.append(", ");
            sql.append("#{gids[").append(i).append("]}");
        }
        
        sql.append(")");
        return sql.toString();
    }
    
    /**
     * 동적 SELECT SQL 생성 (검증용)
     */
    public String selectLayerFeature(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");
        
        // 스키마명이 없으면 기본 스키마 사용
        if (schemaName == null || schemaName.trim().isEmpty()) {
            schemaName = "incheon_geo";
        }
        
        // 테이블명만 있는 경우 처리
        if (tableName == null) {
            tableName = (String) params.get("tableName");
        }
        
        return "SELECT gid, ST_AsGeoJSON(geom) as geometry FROM " + 
               schemaName + "." + tableName + " WHERE gid = #{gid}";
    }
    
    /**
     * 컬럼 데이터 타입 조회
     */
    private String getColumnDataType(List<Map<String, Object>> columns, String columnName) {
        if (columns == null) return "varchar";

        for (Map<String, Object> column : columns) {
            if (columnName.equals(column.get("column_name"))) {
                String dataType = (String) column.get("data_type");
                return DATA_TYPE_MAPPING.getOrDefault(dataType, "varchar");
            }
        }
        return "varchar"; // 기본값
    }

    /**
     * 이력 테이블 INSERT - Added (STTS_CD='C')
     * 새로운 피처 추가 이력 저장
     */
    public String batchInsertHistoryAdded(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> columns = (List<Map<String, Object>>) params.get("columns");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> features = (List<Map<String, Object>>) params.get("features");

        if (features == null || features.isEmpty()) {
            return "SELECT 1 WHERE 1=0";
        }

        // 공통 컬럼 추출 (STTS_CD, 감사 컬럼 제외)
        @SuppressWarnings("unchecked")
        Map<String, Object> firstProperties = (Map<String, Object>) features.get(0).get("properties");

        boolean isGeometryColumn = ObjectUtils.isNotEmpty(features.get(0).get("geometryString"));
        List<String> validColumns = new ArrayList<>();

        // 감사 컬럼 체크
        boolean hasFrstRegId = false;
        boolean hasFrstRegDt = false;

        if (firstProperties != null) {
            for (Map<String, Object> column : columns) {
                String columnName = (String) column.get("column_name");

                // STTS_CD 제외
                if ("stts_cd".equalsIgnoreCase(columnName)) {
                    continue;
                }

                // 감사 컬럼 체크 (INSERT 시)
                if ("frst_reg_id".equalsIgnoreCase(columnName)) {
                    hasFrstRegId = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("frst_reg_dt".equalsIgnoreCase(columnName)) {
                    hasFrstRegDt = true;
                    continue; // validColumns에 추가하지 않음
                }

                if (firstProperties.containsKey(columnName)) {
                    validColumns.add(columnName);
                }
            }
        }

        // 현재 사용자 ID 조회
        String currentUserId = null;
        if (hasFrstRegId) {
            currentUserId = RequestContext.getCurrentUserId();
        }

        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(schemaName).append(".").append(tableName)
           .append(" (").append(isGeometryColumn ? "geom, " : "").append("stts_cd");

        for (String columnName : validColumns) {
            sql.append(", \"").append(columnName).append("\"");
        }

        // 감사 컬럼 추가
        if (hasFrstRegId) {
            sql.append(", frst_reg_id");
        }
        if (hasFrstRegDt) {
            sql.append(", frst_reg_dt");
        }

        sql.append(") VALUES ");

        // 각 피처에 대한 VALUES 생성
        for (int i = 0; i < features.size(); i++) {
            if (i > 0) sql.append(", ");
            if (isGeometryColumn) {
                sql.append("(ST_SetSRID(ST_Force2D(ST_GeomFromGeoJSON(#{features[").append(i).append("].geometryString})), #{srid}), 'C'");
            } else {
                sql.append("('C'");
            }

            for (String columnName : validColumns) {
                String dataType = getColumnDataType(columns, columnName);
                sql.append(", #{features[").append(i).append("].properties.")
                   .append(columnName);

                if (!dataType.equals("varchar")) {
                    sql.append("}::").append(dataType);
                } else {
                    sql.append("}");
                }
            }

            // 감사 컬럼 값 추가
            if (hasFrstRegId) {
                sql.append(", '").append(currentUserId).append("'");
            }
            if (hasFrstRegDt) {
                sql.append(", CURRENT_TIMESTAMP");
            }

            sql.append(")");
        }

        return sql.toString();
    }

    /**
     * 이력 테이블 INSERT - Modified (STTS_CD='U')
     * 피처 수정 이력 저장
     */
    public String batchInsertHistoryModified(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> columns = (List<Map<String, Object>>) params.get("columns");

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> features = (List<Map<String, Object>>) params.get("features");

        if (features == null || features.isEmpty()) {
            return "SELECT 1 WHERE 1=0";
        }

        // 공통 컬럼 추출 (STTS_CD, 감사 컬럼 제외)
        @SuppressWarnings("unchecked")
        Map<String, Object> firstProperties = (Map<String, Object>) features.get(0).get("properties");
        boolean isGeometryColumn = ObjectUtils.isNotEmpty(features.get(0).get("geometryString"));
        List<String> validColumns = new ArrayList<>();

        // 감사 컬럼 체크
        boolean hasFrstRegId = false;
        boolean hasFrstRegDt = false;

        if (firstProperties != null) {
            for (Map<String, Object> column : columns) {
                String columnName = (String) column.get("column_name");

                // STTS_CD 제외
                if ("stts_cd".equalsIgnoreCase(columnName)) {
                    continue;
                }

                // 감사 컬럼 체크 (INSERT 시)
                if ("frst_reg_id".equalsIgnoreCase(columnName)) {
                    hasFrstRegId = true;
                    continue; // validColumns에 추가하지 않음
                }
                if ("frst_reg_dt".equalsIgnoreCase(columnName)) {
                    hasFrstRegDt = true;
                    continue; // validColumns에 추가하지 않음
                }

                if (firstProperties.containsKey(columnName)) {
                    validColumns.add(columnName);
                }
            }
        }

        // 현재 사용자 ID 조회
        String currentUserId = null;
        if (hasFrstRegId) {
            currentUserId = RequestContext.getCurrentUserId();
        }

        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(schemaName).append(".").append(tableName);
        if(isGeometryColumn) {
        	sql.append(" (gid, geom, stts_cd");
        } else {
        	sql.append(" (gid, stts_cd");
        }

        for (String columnName : validColumns) {
            sql.append(", \"").append(columnName).append("\"");
        }

        // 감사 컬럼 추가
        if (hasFrstRegId) {
            sql.append(", frst_reg_id");
        }
        if (hasFrstRegDt) {
            sql.append(", frst_reg_dt");
        }

        sql.append(") VALUES ");

        // 각 피처에 대한 VALUES 생성
        for (int i = 0; i < features.size(); i++) {
            if (i > 0) sql.append(", ");
            sql.append("(#{features[").append(i).append("].gid}, ");
            if(isGeometryColumn) {
                sql.append("ST_SetSRID(ST_Force2D(ST_GeomFromGeoJSON(#{features[").append(i).append("].geometryString})), #{srid}), 'U'");
            } else {
            	sql.append(" 'U'");
            }
            for (String columnName : validColumns) {
                String dataType = getColumnDataType(columns, columnName);
                sql.append(", #{features[").append(i).append("].properties.")
                   .append(columnName);

                if (!dataType.equals("varchar")) {
                    sql.append("}::").append(dataType);
                } else {
                    sql.append("}");
                }
            }

            // 감사 컬럼 값 추가
            if (hasFrstRegId) {
                sql.append(", '").append(currentUserId).append("'");
            }
            if (hasFrstRegDt) {
                sql.append(", CURRENT_TIMESTAMP");
            }

            sql.append(")");
        }

        return sql.toString();
    }

    /**
     * 이력 테이블 INSERT - Deleted (STTS_CD='D')
     * 피처 삭제 이력 저장 (gid만 저장)
     */
    public String batchInsertHistoryDeleted(Map<String, Object> params) {
        String schemaName = (String) params.get("schemaName");
        String tableName = (String) params.get("tableName");

        @SuppressWarnings("unchecked")
        List<Integer> gids = (List<Integer>) params.get("gids");

        if (gids == null || gids.isEmpty()) {
            return "SELECT 1 WHERE 1=0";
        }

        StringBuilder sql = new StringBuilder();
        sql.append("INSERT INTO ").append(schemaName).append(".").append(tableName)
           .append(" (gid, stts_cd) VALUES ");

        for (int i = 0; i < gids.size(); i++) {
            if (i > 0) sql.append(", ");
            sql.append("(#{gids[").append(i).append("]}, 'D')");
        }

        return sql.toString();
    }

}
