package com.erp.service;

import com.erp.common.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 지도 데이터 서비스
 * 외부/내부 DB에서 위도/경도 데이터를 조회하여 지도 마커로 변환
 */
@Service
@Slf4j
public class MapDataService extends BaseService {

    @Autowired private ExternalDbConnectionService externalDbConnectionService;
    @Autowired private JdbcTemplate jdbcTemplate;

    /**
     * 외부 DB에서 지도 데이터 조회
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> getExternalMapData(long connectionId, Map<String, Object> params) {
        String sql = buildSql(params);
        log.info("외부 DB 지도 데이터 조회 SQL: {}", sql);

        Map<String, Object> result = externalDbConnectionService.executeQuery(connectionId, sql);
        List<Map<String, Object>> rows = (List<Map<String, Object>>) result.get("data");
        if (rows == null) rows = Collections.emptyList();

        List<Map<String, Object>> markers = convertToMarkers(rows, params);

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("markers", markers);
        response.put("count", markers.size());
        return response;
    }

    /**
     * 내부 DB에서 지도 데이터 조회
     */
    public Map<String, Object> getInternalMapData(Map<String, Object> params) {
        String sql = buildSql(params);
        log.info("내부 DB 지도 데이터 조회 SQL: {}", sql);

        List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
        List<Map<String, Object>> markers = convertToMarkers(rows, params);

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("markers", markers);
        response.put("count", markers.size());
        return response;
    }

    /**
     * 동적 SELECT SQL 구성
     * tableName, latColumn, lngColumn 필수 / labelColumn, statusColumn, additionalColumns 선택
     */
    private String buildSql(Map<String, Object> params) {
        String tableName = validateIdentifier((String) params.get("table_name"));
        String latColumn = validateIdentifier((String) params.get("lat_column"));
        String lngColumn = validateIdentifier((String) params.get("lng_column"));
        String labelColumn = (String) params.get("label_column");
        String statusColumn = (String) params.get("status_column");
        String additionalColumnsStr = (String) params.get("additional_columns");
        String whereClause = (String) params.get("where_clause");

        // 중복 없는 컬럼 집합 구성
        Set<String> columnSet = new LinkedHashSet<>();
        columnSet.add(latColumn);
        columnSet.add(lngColumn);
        if (labelColumn != null && !labelColumn.isBlank()) {
            columnSet.add(validateIdentifier(labelColumn.trim()));
        }
        if (statusColumn != null && !statusColumn.isBlank()) {
            columnSet.add(validateIdentifier(statusColumn.trim()));
        }
        if (additionalColumnsStr != null && !additionalColumnsStr.isBlank()) {
            for (String col : additionalColumnsStr.split(",")) {
                String trimmed = col.trim();
                if (!trimmed.isBlank()) {
                    columnSet.add(validateIdentifier(trimmed));
                }
            }
        }

        String cols = columnSet.stream()
                .map(c -> "\"" + c + "\"")
                .collect(Collectors.joining(", "));

        String sql = "SELECT " + cols + " FROM \"" + tableName + "\"";
        if (whereClause != null && !whereClause.isBlank()) {
            sql += " WHERE " + whereClause;
        }
        return sql;
    }

    /**
     * 식별자(테이블명/컬럼명) 유효성 검증 — SQL 인젝션 방지
     */
    private String validateIdentifier(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("테이블명/컬럼명이 비어 있습니다.");
        }
        if (!name.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
            throw new IllegalArgumentException("유효하지 않은 식별자: " + name);
        }
        return name;
    }

    /**
     * DB rows → MapMarker 변환
     * 유효하지 않은 좌표(null, NaN, 범위 초과)는 스킵
     */
    private List<Map<String, Object>> convertToMarkers(
            List<Map<String, Object>> rows, Map<String, Object> params) {

        String latColumn = (String) params.get("lat_column");
        String lngColumn = (String) params.get("lng_column");
        String labelColumn = (String) params.get("label_column");
        String statusColumn = (String) params.get("status_column");
        String additionalColumnsStr = (String) params.get("additional_columns");

        List<String> additionalColumns = new ArrayList<>();
        if (additionalColumnsStr != null && !additionalColumnsStr.isBlank()) {
            for (String col : additionalColumnsStr.split(",")) {
                String trimmed = col.trim();
                if (!trimmed.isBlank()) additionalColumns.add(trimmed);
            }
        }

        List<Map<String, Object>> markers = new ArrayList<>();

        for (int i = 0; i < rows.size(); i++) {
            Map<String, Object> row = rows.get(i);

            Double lat = extractCoordinate(row, latColumn);
            Double lng = extractCoordinate(row, lngColumn);

            if (lat == null || lng == null) continue;
            if (lat < -90 || lat > 90) continue;
            if (lng < -180 || lng > 180) continue;

            Map<String, Object> marker = new LinkedHashMap<>();

            Object id = row.get("id");
            if (id == null) id = row.get("ID");
            if (id == null) id = "marker-" + i;
            marker.put("id", id);
            marker.put("latitude", lat);
            marker.put("longitude", lng);

            if (labelColumn != null && !labelColumn.isBlank() && row.get(labelColumn) != null) {
                marker.put("label", row.get(labelColumn));
            }
            if (statusColumn != null && !statusColumn.isBlank() && row.get(statusColumn) != null) {
                marker.put("status", row.get(statusColumn));
            }

            if (!additionalColumns.isEmpty()) {
                Map<String, Object> additionalInfo = new LinkedHashMap<>();
                for (String col : additionalColumns) {
                    if (row.get(col) != null) {
                        additionalInfo.put(col, row.get(col));
                    }
                }
                if (!additionalInfo.isEmpty()) {
                    marker.put("additional_info", additionalInfo);
                }
            }

            markers.add(marker);
        }

        return markers;
    }

    /**
     * 좌표값 추출 (Number 또는 String → Double)
     */
    private Double extractCoordinate(Map<String, Object> row, String columnName) {
        Object value = row.get(columnName);
        if (value == null) return null;
        if (value instanceof Number) return ((Number) value).doubleValue();
        if (value instanceof String) {
            try {
                return Double.parseDouble((String) value);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }
}
