package incheon.ags.mrb.style.utils;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import incheon.ags.mrb.style.vo.JsonStyleVO;

/**
 * SLD XML 파싱 유틸리티
 * XML에서 JsonStyleVO 객체로 변환하는 파싱 로직 담당
 */
public class SldParser {

    /**
     * 단일 스타일 파싱
     */
    public static JsonStyleVO.StyleConfig parseSingleStyle(Element rule, String layerType) {
        return parseSingleStyle(rule, layerType, null, 0);
    }

    /**
     * 단일 스타일 파싱 (icon_info 포함)
     */
    public static JsonStyleVO.StyleConfig parseSingleStyle(Element rule, String layerType, java.util.Map<String, Object> iconInfoMap, int index) {
        JsonStyleVO.StyleConfig styleConfig = new JsonStyleVO.StyleConfig();

        // Symbolizer 타입에 따라 파싱
        Element symbolizer = findSymbolizerElement(rule, layerType);
        if (symbolizer != null) {
            styleConfig.setSymbol(parseSymbol(symbolizer, layerType, iconInfoMap, index));
        }

        // TextSymbolizer (라벨) 파싱
        Element textSymbolizer = XmlUtils.getFirstElementByTagName(rule, "TextSymbolizer");
        if (textSymbolizer != null) {
            styleConfig.setLabel(parseTextSymbol(textSymbolizer));
        }

        return styleConfig;
    }

    /**
     * 레이어 타입에 맞는 Symbolizer Element 찾기
     */
    private static Element findSymbolizerElement(Element rule, String layerType) {
        String type = layerType != null ? layerType.toLowerCase() : "";

        if (type.contains("point")) {
            return XmlUtils.getFirstElementByTagName(rule, "PointSymbolizer");
        } else if (type.contains("line") || type.equals("multiline")) {
            return XmlUtils.getFirstElementByTagName(rule, "LineSymbolizer");
        } else if (type.contains("polygon")) {
            return XmlUtils.getFirstElementByTagName(rule, "PolygonSymbolizer");
        }

        return null;
    }

    /**
     * 심볼 파싱 (통합)
     */
    private static JsonStyleVO.SymbolConfig parseSymbol(Element symbolizer, String layerType) {
        return parseSymbol(symbolizer, layerType, null, 0);
    }

    /**
     * 심볼 파싱 (통합) - icon_info 포함
     */
    private static JsonStyleVO.SymbolConfig parseSymbol(Element symbolizer, String layerType, java.util.Map<String, Object> iconInfoMap, int index) {
        String type = layerType != null ? layerType.toLowerCase() : "";

        if (type.contains("point")) {
            return parsePointSymbol(symbolizer, iconInfoMap, index);
        } else if (type.contains("line") || type.equals("multiline")) {
            return parseLineSymbol(symbolizer);
        } else if (type.contains("polygon")) {
            return parsePolygonSymbol(symbolizer);
        }

        return new JsonStyleVO.SymbolConfig();
    }

    /**
     * Point 심볼 파싱
     */
    private static JsonStyleVO.SymbolConfig parsePointSymbol(Element pointSymbolizer) {
        return parsePointSymbol(pointSymbolizer, null, 0);
    }

    /**
     * Point 심볼 파싱 (icon_info 포함)
     */
    private static JsonStyleVO.SymbolConfig parsePointSymbol(Element pointSymbolizer, java.util.Map<String, Object> iconInfoMap, int index) {
        JsonStyleVO.SymbolConfig symbol = new JsonStyleVO.SymbolConfig();
        symbol.setType("circle");

        Element graphic = XmlUtils.getFirstElementByTagName(pointSymbolizer, "Graphic");
        if (graphic == null) return symbol;

        // ExternalGraphic (이미지) 확인
        Element externalGraphic = XmlUtils.getFirstElementByTagName(graphic, "ExternalGraphic");
        if (externalGraphic != null) {
            parseImageConfig(externalGraphic, symbol, iconInfoMap, index);
            // ExternalGraphic이 있어도 Size는 파싱해야 함 (early return 제거)
        } else {
            // Mark (기본 도형) 확인
            Element mark = XmlUtils.getFirstElementByTagName(graphic, "Mark");
            if (mark != null) {
                parseMarkConfig(mark, symbol);
            }
        }

        // Size 파싱 (ExternalGraphic, Mark 모두 공통 적용)
        Element size = XmlUtils.getFirstElementByTagName(graphic, "Size");
        if (size != null) {
            symbol.setSize(parseIntValue(size.getTextContent(), 14));
        }

        return symbol;
    }

    /**
     * Line 심볼 파싱
     */
    private static JsonStyleVO.SymbolConfig parseLineSymbol(Element lineSymbolizer) {
        JsonStyleVO.SymbolConfig symbol = new JsonStyleVO.SymbolConfig();
        symbol.setType("line");

        Element stroke = XmlUtils.getFirstElementByTagName(lineSymbolizer, "Stroke");
        if (stroke != null) {
            JsonStyleVO.StrokeConfig strokeConfig = parseStrokeConfig(stroke);
            symbol.setStroke(strokeConfig);

            // 선의 색상을 심볼 색상으로도 설정
            if (strokeConfig.getColor() != null) {
                symbol.setColor(strokeConfig.getColor());
            }
            if (strokeConfig.getOpacity() != null) {
                symbol.setOpacity(strokeConfig.getOpacity());
            }
        }

        return symbol;
    }

    /**
     * Polygon 심볼 파싱
     */
    private static JsonStyleVO.SymbolConfig parsePolygonSymbol(Element polygonSymbolizer) {
        JsonStyleVO.SymbolConfig symbol = new JsonStyleVO.SymbolConfig();
        symbol.setType("polygon");

        // Fill 파싱
        Element fill = XmlUtils.getFirstElementByTagName(polygonSymbolizer, "Fill");
        if (fill != null) {
            parseFillConfig(fill, symbol);
        }

        // Stroke 파싱
        Element stroke = XmlUtils.getFirstElementByTagName(polygonSymbolizer, "Stroke");
        if (stroke != null) {
            symbol.setStroke(parseStrokeConfig(stroke));
        }

        return symbol;
    }

    /**
     * 이미지 설정 파싱
     */
    private static void parseImageConfig(Element externalGraphic, JsonStyleVO.SymbolConfig symbol) {
        parseImageConfig(externalGraphic, symbol, null, 0);
    }

    /**
     * 이미지 설정 파싱 (icon_info 포함)
     */
    private static void parseImageConfig(Element externalGraphic, JsonStyleVO.SymbolConfig symbol, java.util.Map<String, Object> iconInfoMap, int index) {
        Element onlineResource = XmlUtils.getFirstElementByTagName(externalGraphic, "OnlineResource");
        if (onlineResource != null) {
            String href = onlineResource.getAttribute("xlink:href");
            if (href != null && !href.isEmpty()) {
                // URL에서 /resources 이후 경로만 추출
                String iconPath = href;
                if (href.contains("/resources")) {
                    iconPath = href.substring(href.indexOf("/resources"));
                }

                // ExternalGraphic이 있으면 type을 userIcon으로 설정
                symbol.setType("userIcon");

                // icon_info Map에서 해당 인덱스의 iconId 추출
                if (iconInfoMap != null && !iconInfoMap.isEmpty()) {
                    String indexKey = String.valueOf(index);
                    Object iconIdObj = iconInfoMap.get(indexKey);
                    if (iconIdObj != null) {
                        String iconId = String.valueOf(iconIdObj);
                        symbol.setIconId(iconId);
                    }
                }

                JsonStyleVO.ImageConfig imageConfig = new JsonStyleVO.ImageConfig();
                imageConfig.setSrc(iconPath);
                symbol.setImage(imageConfig);
            }
        }
    }

    /**
     * Mark (도형) 설정 파싱
     */
    private static void parseMarkConfig(Element mark, JsonStyleVO.SymbolConfig symbol) {
        // WellKnownName
        Element wellKnownName = XmlUtils.getFirstElementByTagName(mark, "WellKnownName");
        if (wellKnownName != null) {
            symbol.setType(wellKnownName.getTextContent());
        }

        // Fill
        Element fill = XmlUtils.getFirstElementByTagName(mark, "Fill");
        if (fill != null) {
            parseFillConfig(fill, symbol);
        }

        // Stroke
        Element stroke = XmlUtils.getFirstElementByTagName(mark, "Stroke");
        if (stroke != null) {
            symbol.setStroke(parseStrokeConfig(stroke));
        }
    }

    /**
     * Fill 설정 파싱
     */
    private static void parseFillConfig(Element fill, JsonStyleVO.SymbolConfig symbol) {
        NodeList cssParams = fill.getElementsByTagNameNS("*", "CssParameter");
        for (int i = 0; i < cssParams.getLength(); i++) {
            Element param = (Element) cssParams.item(i);
            String name = param.getAttribute("name");
            String value = param.getTextContent();

            switch (name) {
                case "fill":
                    symbol.setColor(value);
                    break;
                case "fill-opacity":
                    symbol.setOpacity(parseDoubleValue(value, 1.0));
                    break;
            }
        }
    }

    /**
     * Stroke 설정 파싱
     */
    public static JsonStyleVO.StrokeConfig parseStrokeConfig(Element stroke) {
        JsonStyleVO.StrokeConfig strokeConfig = new JsonStyleVO.StrokeConfig();
        strokeConfig.setStyle("solid");

        NodeList cssParams = stroke.getElementsByTagNameNS("*", "CssParameter");
        for (int i = 0; i < cssParams.getLength(); i++) {
            Element param = (Element) cssParams.item(i);
            String name = param.getAttribute("name");
            String value = param.getTextContent();

            switch (name) {
                case "stroke":
                    strokeConfig.setColor(value);
                    break;
                case "stroke-width":
                    strokeConfig.setWidth(parseIntValue(value, 1));
                    break;
                case "stroke-opacity":
                    strokeConfig.setOpacity(parseDoubleValue(value, 1.0));
                    break;
                case "stroke-dasharray":
                    // style 필드 설정
                    strokeConfig.setStyle(parseDashArrayToStyle(value));
                    // dashArray 필드도 설정 (원본 값 보존)
                    strokeConfig.setDashArray(parseDashArrayToDoubleArray(value));
                    break;
                case "stroke-linecap":
                    strokeConfig.setLineCap(value);
                    break;
                case "stroke-linejoin":
                    strokeConfig.setLineJoin(value);
                    break;
            }
        }

        return strokeConfig;
    }

    /**
     * TextSymbolizer 파싱
     */
    public static JsonStyleVO.LabelConfig parseTextSymbol(Element textSymbolizer) {
        JsonStyleVO.LabelConfig labelConfig = new JsonStyleVO.LabelConfig();

        // Label (필드) 파싱
        parseLabelField(textSymbolizer, labelConfig);

        // Font 파싱
        parseFont(textSymbolizer, labelConfig);

        // Fill (텍스트 색상) 파싱
        parseTextFill(textSymbolizer, labelConfig);

        // Halo (외곽선) 파싱
        parseHalo(textSymbolizer, labelConfig);

        // Background (배경) 파싱
        parseBackground(textSymbolizer, labelConfig);

        // LabelPlacement (오프셋) 파싱
        parseLabelPlacement(textSymbolizer, labelConfig);

        return labelConfig;
    }

    /**
     * 라벨 필드 파싱
     */
    private static void parseLabelField(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        Element label = XmlUtils.getFirstElementByTagName(textSymbolizer, "Label");
        if (label != null) {
            Element propertyName = XmlUtils.getFirstElementByTagName(label, "PropertyName");
            if (propertyName != null) {
                labelConfig.setField(propertyName.getTextContent());
            }
        }
    }

    /**
     * 폰트 파싱
     */
    private static void parseFont(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        Element font = XmlUtils.getFirstElementByTagName(textSymbolizer, "Font");
        if (font == null) return;

        String fontFamily = "NanumGothic";
        Integer fontSize = 14;

        NodeList cssParams = font.getElementsByTagNameNS("*", "CssParameter");
        for (int i = 0; i < cssParams.getLength(); i++) {
            Element param = (Element) cssParams.item(i);
            String name = param.getAttribute("name");
            String value = param.getTextContent();

            switch (name) {
                case "font-family":
                    fontFamily = value;
                    break;
                case "font-size":
                    try {
                        fontSize = Integer.parseInt(value.replaceAll("[^0-9]", ""));
                    } catch (NumberFormatException e) {
                        fontSize = 14;
                    }
                    break;
            }
        }

        // font와 size를 개별 필드로 설정
        labelConfig.setFont(fontFamily);
        labelConfig.setSize(fontSize);
    }

    /**
     * 텍스트 Fill 파싱
     */
    private static void parseTextFill(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        Element fill = XmlUtils.getDirectChildElementByTagName(textSymbolizer, "Fill");
        if (fill == null) return;

        NodeList cssParams = fill.getElementsByTagNameNS("*", "CssParameter");
        for (int i = 0; i < cssParams.getLength(); i++) {
            Element param = (Element) cssParams.item(i);
            String name = param.getAttribute("name");
            String value = param.getTextContent();

            if ("fill".equals(name)) {
                labelConfig.setColor(value);
            }
        }
    }

    /**
     * Halo 파싱
     */
    private static void parseHalo(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        Element halo = XmlUtils.getFirstElementByTagName(textSymbolizer, "Halo");
        if (halo == null) return;

        JsonStyleVO.HaloConfig haloConfig = new JsonStyleVO.HaloConfig();

        // Radius (두께)
        Element radius = XmlUtils.getFirstElementByTagName(halo, "Radius");
        if (radius != null) {
            haloConfig.setWidth(parseIntValue(radius.getTextContent(), 2));
        }

        // Fill (색상)
        Element haloFill = XmlUtils.getFirstElementByTagName(halo, "Fill");
        if (haloFill != null) {
            NodeList cssParams = haloFill.getElementsByTagNameNS("*", "CssParameter");
            for (int i = 0; i < cssParams.getLength(); i++) {
                Element param = (Element) cssParams.item(i);
                String name = param.getAttribute("name");
                String value = param.getTextContent();

                if ("fill".equals(name)) {
                    haloConfig.setColor(value);
                }
            }
        }

        labelConfig.setHalo(haloConfig);
    }

    /**
     * Background 파싱
     *
     * 주의: SLD 1.0.0 표준에는 TextSymbolizer 배경이 없습니다.
     * VendorOption이 있는 경우에만 파싱 시도
     */
    private static void parseBackground(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        // VendorOption 확인
        NodeList vendorOptions = textSymbolizer.getElementsByTagNameNS("*", "VendorOption");
        if (vendorOptions.getLength() == 0) return;

        JsonStyleVO.BackgroundConfig backgroundConfig = new JsonStyleVO.BackgroundConfig();
        boolean hasBackground = false;

        for (int i = 0; i < vendorOptions.getLength(); i++) {
            Element option = (Element) vendorOptions.item(i);
            String name = option.getAttribute("name");
            String value = option.getTextContent();

            // GeoServer VendorOption 예시 (확장 필요)
            if ("background-color".equals(name)) {
                backgroundConfig.setColor(value);
                hasBackground = true;
            } else if ("background-opacity".equals(name)) {
                backgroundConfig.setOpacity(parseDoubleValue(value, 1.0));
                hasBackground = true;
            }
        }

        if (hasBackground) {
            labelConfig.setBackground(backgroundConfig);
        }
    }

    /**
     * LabelPlacement 파싱
     */
    private static void parseLabelPlacement(Element textSymbolizer, JsonStyleVO.LabelConfig labelConfig) {
        Element labelPlacement = XmlUtils.getFirstElementByTagName(textSymbolizer, "LabelPlacement");
        if (labelPlacement == null) return;

        Element pointPlacement = XmlUtils.getFirstElementByTagName(labelPlacement, "PointPlacement");
        if (pointPlacement == null) return;

        Element displacement = XmlUtils.getFirstElementByTagName(pointPlacement, "Displacement");
        // Displacement가 없으면 offset 설정하지 않음
        if (displacement == null) return;

        Element displacementX = XmlUtils.getFirstElementByTagName(displacement, "DisplacementX");
        Element displacementY = XmlUtils.getFirstElementByTagName(displacement, "DisplacementY");

        Double offsetX = parseDoubleValue(displacementX != null ? displacementX.getTextContent() : "0", 0.0);
        Double offsetY = parseDoubleValue(displacementY != null ? displacementY.getTextContent() : "0", 0.0);

        labelConfig.setOffset(new Double[]{offsetX, offsetY});
    }

    /**
     * 줌 레벨 파싱
     */
    public static JsonStyleVO.DisplayLevelConfig parseDisplayLevel(Element rule) {
        Element minScale = XmlUtils.getFirstElementByTagName(rule, "MinScaleDenominator");
        Element maxScale = XmlUtils.getFirstElementByTagName(rule, "MaxScaleDenominator");

        if (minScale == null && maxScale == null) return null;

        JsonStyleVO.DisplayLevelConfig displayLevel = new JsonStyleVO.DisplayLevelConfig();

        if (minScale != null) {
            double scaleDenominator = parseDoubleValue(minScale.getTextContent(), 0.0);
            displayLevel.setMaxZoom(scaleToZoom(scaleDenominator));
        }

        if (maxScale != null) {
            double scaleDenominator = parseDoubleValue(maxScale.getTextContent(), 0.0);
            displayLevel.setMinZoom(scaleToZoom(scaleDenominator));
        }

        return displayLevel;
    }

    /**
     * Scale Denominator를 줌 레벨로 변환
     */
    private static int scaleToZoom(double scaleDenominator) {
        if (scaleDenominator <= 0) return 0;

        double zoom = Math.log(559082264.0 / scaleDenominator) / Math.log(2);
        return Math.max(0, Math.min(22, (int) Math.round(zoom)));
    }

    /**
     * 대시 배열을 스타일로 변환
     */
    private static String parseDashArrayToStyle(String dashArray) {
        if (dashArray == null || dashArray.trim().isEmpty()) {
            return "solid";
        }

        // SldConstants.DashArrayStyle enum 활용
        if ("1 2".equals(dashArray)) return "dotted";
        if ("5 5".equals(dashArray)) return "dashed";

        return "solid";
    }

    /**
     * 대시 배열 문자열을 Double 배열로 변환
     */
    private static Double[] parseDashArrayToDoubleArray(String dashArray) {
        if (dashArray == null || dashArray.trim().isEmpty()) {
            return null;
        }

        try {
            String[] parts = dashArray.trim().split("\\s+");
            Double[] result = new Double[parts.length];
            for (int i = 0; i < parts.length; i++) {
                result[i] = Double.parseDouble(parts[i]);
            }
            return result;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Integer 파싱
     */
    private static int parseIntValue(String value, int defaultValue) {
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }

    /**
     * Double 파싱
     */
    private static double parseDoubleValue(String value, double defaultValue) {
        try {
            return Double.parseDouble(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}
