
import os
import re


from qgis.PyQt import QtWidgets, uic
from qgis.PyQt.QtWidgets import (
    QWidget, QDialog, QMessageBox, QTableWidgetItem, QLineEdit
)
from qgis.core import (
    QgsVectorLayer, QgsExpression, QgsExpressionContext, QgsExpressionContextUtils
)


import common
from urbanq.logging.logging_config import logger
from urbanq.function.widgetutils import show_progress



FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'fileSetting_dockwidget_base.ui'))


class fileSettingDockWidget(QDialog, FORM_CLASS):  
    def __init__(self, parent=None, option=None):
        
        super(fileSettingDockWidget, self).__init__(parent)  
        
        
        
        
        
        self.setupUi(self)

        
        show_progress(self.progressBar, False)

        
        
        
        self.setting_by_text = option["setting_by_text"]
        self.setting_by_array = option["setting_by_array"]
        self.setting_by_expression = option["setting_by_expression"]
        self.setting_by_section = option["setting_by_section"]["enabled"]
        self.setting_by_numeric = option["setting_by_numeric"]["enabled"]
        self.setting_by_combo = option["setting_by_combo"]["enabled"]

        
        self.textWidget.setVisible(self.setting_by_text)
        self.arrayWidget.setVisible(self.setting_by_array)
        self.expressionWidget.setVisible(self.setting_by_expression)
        self.sectionWidget.setVisible(self.setting_by_section)
        self.numericWidget.setVisible(self.setting_by_numeric)
        self.comboWidget.setVisible(self.setting_by_combo)

        
        if option["setting_by_numeric"]["value_type"] == "int":
            self.numericValue.setDecimals(0)

        
        if option["setting_by_section"]["value_type"] == "int":
            self.section_min.setDecimals(0)
            self.section_max.setDecimals(0)

        
        if option["setting_by_combo"]["items"]:
            self.comboBoxValue.addItems(option["setting_by_combo"]["items"])

        
        for widget in [
            self.expression_type_1, self.expression_type_2, self.expression_type_3, self.expression_type_4,
            self.expression_type_5, self.expression_type_6, self.expression_type_7, self.expression_type_8,
            self.expression_type_9, self.expression_type_10, self.expression_type_11, self.expression_type_12,

            self.qlabel_9165, self.qlabel_4851, self.qlabel_4852, self.qlabel_4849, self.qlabel_4848,
            self.qlabel_4842, self.qlabel_4843, self.qlabel_4844, self.qlabel_9166,
        ]:
            widget.setProperty("class", "mediumText")

        for widget in [
            self.qlabel_8913, self.qlabel_2570, self.qlabel_2571, self.qlabel_2569, self.qlabel_2568, self.qlabel_8914,
            self.qlabel_3542, self.qlabel_8763, self.qlabel_8764, self.qlabel_8762, self.qlabel_8761, self.qlabel_3543,
            self.expressionPushButton
        ]:
            widget.setProperty("class", "boldText")

        
        self.update_visible_step_labels([self.qlabel_8913, self.qlabel_2570, self.qlabel_2571, self.qlabel_2569, self.qlabel_2568, self.qlabel_8914])

        
        self.pages_mapping_connect([
            self.expression_type_1, self.expression_type_2, self.expression_type_3, self.expression_type_4,
            self.expression_type_5, self.expression_type_6, self.expression_type_7, self.expression_type_8,
            self.expression_type_9, self.expression_type_10, self.expression_type_11, self.expression_type_12,
        ], self.stackedWidget)

        
        self.label_groups = {
            "SETTING_TEXT": [
                self.qlabel_3542, self.qlabel_9165, self.qlabel_9167
            ],
            "SETTING_EXPRESSION": [
                self.qlabel_3543, self.qlabel_9166, self.qlabel_9168
            ],
            "SETTING_ARRAY": [
                self.qlabel_8761, self.qlabel_4842, self.qlabel_4843, self.qlabel_4844, self.qlabel_4845
            ],
            "SETTING_SECTION": [
                self.qlabel_8762, self.qlabel_4849, self.qlabel_4848, self.qlabel_4846
            ],
            "SETTING_NUMERIC": [
                self.qlabel_8763, self.qlabel_4851, self.qlabel_4847
            ],
            "SETTING_COMBO": [
                self.qlabel_8764, self.qlabel_4852, self.qlabel_4850
            ]
        }
        self.update_label_texts(option, self.label_groups)

        
        self._inserting_field = False
        self._previous_expression = ""
        self.connect_expression_text_changed()

        
        self.set_expression_widget_layer()

        
        common.signals.file_preview_updated.connect(self.set_expression_widget_layer)

        
        self.expressionPushButton.clicked.connect(lambda: self.validate_expression(True))

        
        
        self.stackedWidget.setCurrentIndex(0)

    @staticmethod
    def pages_mapping_connect(buttons, stacked_widget):
        
        for index, button in enumerate(buttons):
            button.toggled.connect(
                lambda is_checked, page_index=index: stacked_widget.setCurrentIndex(page_index) if is_checked else None)

    @staticmethod
    def update_label_texts(option, label_groups):
        
        def is_nested_list(obj):
            return isinstance(obj, list) and any(isinstance(i, list) for i in obj)

        for key, labels_list in label_groups.items():
            texts = option.get(key, [])  
            if not texts:
                continue  

            if is_nested_list(labels_list):
                for i, labels in enumerate(labels_list):
                    for j, label in enumerate(labels):
                        if j < len(texts) and texts[j] and texts[j].strip():
                            label.setText(texts[j])
            else:
                for i, label in enumerate(labels_list):
                    if i < len(texts) and texts[i] and texts[i].strip():
                        label.setText(texts[i])

    def update_visible_step_labels(self, label_list):
        
        try:
            step = 1
            for label in label_list:
                if label.isVisibleTo(self):
                    label.setText(f" STEP {step} ")

                    
                    top_margin = 0 if step == 1 else 30
                    label.parent().layout().setContentsMargins(0, top_margin, 0, 6)
                    step += 1

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def connect_expression_text_changed(self):
        
        try:
            expr_line_edit = self.FieldExpressionWidget.findChild(QLineEdit)
            if expr_line_edit:
                expr_line_edit.textChanged.connect(self.on_expression_text_changed)

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def on_expression_text_changed(self, new_text):
        
        try:
            if self._inserting_field:
                return

            layer = self.FieldExpressionWidget.layer()
            if not layer:
                return

            field_names = [f.name() for f in layer.fields()]

            
            if new_text in field_names:
                self._inserting_field = True

                expr_line_edit = self.FieldExpressionWidget.findChild(QLineEdit)
                quoted_field = f'"{new_text}"'

                
                old_expr = self._previous_expression.strip()
                joiner = " " if old_expr and not old_expr.endswith(('(', '+', '-', '*', '/')) else ""
                new_expr = old_expr + joiner + quoted_field

                expr_line_edit.setText(new_expr)
                expr_line_edit.setCursorPosition(len(new_expr))

                self._previous_expression = new_expr  
                self._inserting_field = False
            else:
                self._previous_expression = new_text  

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def set_expression_widget_layer(self):
        
        try:
            
            source_file_type, source_file_path, _ = common.fileInfo_1.file_record.get_record()
            if source_file_type == "layer" and isinstance(source_file_path, QgsVectorLayer):
                self.FieldExpressionWidget.setExpression("")
                self.FieldExpressionWidget.setLayer(source_file_path)

            
            else:
                self.FieldExpressionWidget.setExpression("")
                self.FieldExpressionWidget.setLayer(None)

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def validate_expression(self, is_visible=True):
        
        try:
            
            layer = self.FieldExpressionWidget.layer()
            if layer is None:
                QMessageBox.warning(self, "레이어 미설정", "표현식 검사를 위해서는 레이어가 먼저 설정되어야 합니다.")
                return False

            
            expr_text = self.FieldExpressionWidget.expression()
            expr = QgsExpression(expr_text)

            
            if expr.hasParserError():
                QMessageBox.warning(self, "표현식 오류", f"표현식이 잘못되었습니다:\n{expr.parserErrorString()}")
                return False

            
            referenced_fields = expr.referencedColumns()
            layer_fields = [field.name() for field in layer.fields()]
            missing_fields = [f for f in referenced_fields if f not in layer_fields]

            if missing_fields:
                QMessageBox.warning(self, "필드 오류", f"표현식에 존재하지 않는 필드가 포함되어 있습니다:\n{', '.join(missing_fields)}")
                return False

            
            sample_features = layer.getFeatures()
            first_feature = next(sample_features, None)
            if first_feature:
                context = QgsExpressionContext()
                context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                context.setFeature(first_feature)

                expr.prepare(context)
                expr.evaluate(context)

                if expr.hasEvalError():
                    QMessageBox.warning(self, "평가 오류", f"표현식 실행 중 오류 발생:\n{expr.evalErrorString()}")
                    return False

            
            if is_visible:
                QMessageBox.information(self, "확인", "유효한 표현식입니다.")

            return True

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)
            return False

    def set_fileResults(self, num=1):
        
        try:
            setting_text = None
            setting_array_string = None
            setting_array_integer = None
            setting_array_float = None
            setting_expression = None
            setting_section_min = None
            setting_section_max = None
            setting_numeric = None
            setting_combo = None

            
            if self.textValue.isVisibleTo(self):
                setting_text = self.textValue.text()
                if not setting_text:
                    QMessageBox.warning(self, "입력 필요", "작업에 필요한 값을 입력해 주세요.")
                    return False

            
            if self.comboBoxValue.isVisibleTo(self):
                setting_combo = self.comboBoxValue.currentText()
                if not setting_combo:
                    QMessageBox.warning(self, "입력 필요", "작업에 필요한 리스트 값을 선택해 주세요")
                    return False

            
            if self.array_string.isVisibleTo(self):
                setting_array_string = self.array_string.text()
                if not setting_array_string:
                    QMessageBox.warning(self, "입력 필요", "문자열(String) 값을 입력해 주세요.")
                    return False

            
            if self.array_int.isVisibleTo(self):
                setting_array_integer = self.array_int.text()
                if not setting_array_integer:
                    QMessageBox.warning(self, "입력 필요", "정수(Integer) 값을 입력해 주세요.")
                    return False

            
            if self.array_float.isVisibleTo(self):
                setting_array_float = self.array_float.text()
                if not setting_array_float:
                    QMessageBox.warning(self, "입력 필요", "실수(Float) 값을 입력해 주세요.")
                    return False

            
            
            setting_numeric = self.numericValue.text()
            setting_section_min = self.section_min.value()
            setting_section_max = self.section_max.value()

            
            if self.FieldExpressionWidget.isVisibleTo(self):

                
                if not self.validate_expression(False):
                    return False

                setting_expression = self.FieldExpressionWidget.expression()
                if not setting_expression:
                    QMessageBox.warning(self, "입력 필요", "조건식을 입력해 주세요.")
                    return False

            
            if self.setting_by_text:
                common.fileInfo_1.file_setting.set_text(setting_text)

            
            if self.setting_by_array:
                common.fileInfo_1.file_setting.set_array(setting_array_string, setting_array_integer, setting_array_float)

            
            if self.setting_by_expression:
                common.fileInfo_1.file_setting.set_expression(setting_expression)

            
            if self.setting_by_section:
                common.fileInfo_1.file_setting.set_section(setting_section_min, setting_section_max)

            
            if self.setting_by_numeric:
                common.fileInfo_1.file_setting.set_numeric(setting_numeric)

            
            if self.setting_by_combo:
                common.fileInfo_1.file_setting.set_combo(setting_combo)

            

            return True

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)
            return False


