
import os
import re
import csv
from collections import Counter


from qgis.PyQt import QtWidgets, uic
from qgis.PyQt.QtWidgets import (
    QWidget, QDialog, QMessageBox,
    QTableWidgetItem, QFileDialog, QTableWidget
)
from qgis.gui import QgsFileWidget


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__), 'fileSave_dockwidget_base.ui'))


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

        
        show_progress(self.progressBar, False)

        
        
        
        self.output_by_file = option["output_by_file"]
        self.output_by_field = option["output_by_field"]
        self.output_by_table = option["output_by_table"]
        self.fold_to_file = option.get("fold_to_file", False)
        self.file_to_fold = option.get("file_to_fold", False)

        
        self.fileTypeWidget.setVisible(self.output_by_file)
        self.pathWidget.setVisible(self.output_by_file)
        self.pathInfoWidget.setVisible(self.output_by_file)
        self.fieldWidget.setVisible(self.output_by_field)
        self.tableWidget.setVisible(self.output_by_table)

        
        for widget in [
            self.file_type_shp, self.file_type_txt, self.file_type_csv, self.file_type_json, self.file_type_geojson,
            self.file_type_fold, self.file_type_layer, self.tablePushButton,

            self.qlabel_3572, self.qlabel_3573, self.qlabel_3574, self.qlabel_3575,
            self.qlabel_3576, self.qlabel_3577, self.qlabel_9165,
        ]:
            widget.setProperty("class", "mediumText")

        for widget in [
            self.qlabel_4071, self.qlabel_8913, self.qlabel_2791, self.qlabel_1073,
            self.qlabel_3542, self.qlabel_2704, self.qlabel_5044,  self.qlabel_8388,
        ]:
            widget.setProperty("class", "boldText")

        
        self.label_groups = {
            "RESULT_FIELD": [
                [self.qlabel_3542, self.qlabel_9165, self.qlabel_9166],
            ],
            "RESULT_TABLE": [
                [self.qlabel_2704, self.qlabel_2705],
            ],
        }
        self.update_label_texts(option, self.label_groups)

        
        self.shpPathWidget.setFilter("Shapefiles (*.shp)")
        self.jsonPathWidget.setFilter("JSON Files (*.json)")
        self.geojsonPathWidget.setFilter("GeoJSON Files (*.geojson)")
        self.txtPathWidget.setFilter("Text Files (*.txt)")
        self.csvPathWidget.setFilter("CSV Files (*.csv)")
        self.foldPathWidget.setFilter("Folders (*)")
        self.foldPathWidget.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)

        self.input_mapping_shp = {
            "file_type": self.file_type_shp,
            "file_widget": self.shpPathWidget,
        }
        self.input_mapping_json = {
            "file_type": self.file_type_json,
            "file_widget": self.jsonPathWidget,
        }
        self.input_mapping_geojson = {
            "file_type": self.file_type_geojson,
            "file_widget": self.geojsonPathWidget,
        }
        self.input_mapping_txt = {
            "file_type": self.file_type_txt,
            "file_widget": self.txtPathWidget,
        }
        self.input_mapping_csv = {
            "file_type": self.file_type_csv,
            "file_widget": self.csvPathWidget,
        }
        self.input_mapping_fold = {
            "file_type": self.file_type_fold,
            "file_widget": self.foldPathWidget,
        }

        
        self.disable_map = {
            'layer': [self.file_type_fold],
            'shp': [self.file_type_layer, self.file_type_fold],
            'geojson': [self.file_type_layer, self.file_type_fold],
            'txt': [self.file_type_shp, self.file_type_layer, self.file_type_geojson, self.file_type_fold],
            'csv': [self.file_type_shp, self.file_type_layer, self.file_type_geojson, self.file_type_fold],
            'json': [self.file_type_shp, self.file_type_layer, self.file_type_geojson, self.file_type_fold],
            'folder': [self.file_type_shp, self.file_type_layer, self.file_type_geojson,
                       self.file_type_txt, self.file_type_csv, self.file_type_json],
            'all': [self.file_type_shp, self.file_type_layer, self.file_type_geojson,
                    self.file_type_txt, self.file_type_csv, self.file_type_json, self.file_type_fold],
        }

        
        if self.fold_to_file:
            self.disable_map['folder'] = [self.file_type_shp, self.file_type_layer, self.file_type_geojson,
                                          self.file_type_json, self.file_type_fold]

        
        if self.file_to_fold:
            temp = [
                self.file_type_shp,
                self.file_type_layer,
                self.file_type_geojson,
                self.file_type_txt,
                self.file_type_csv,
                self.file_type_json,
            ]

            for key in ('layer', 'shp', 'geojson', 'txt', 'csv', 'json'):
                self.disable_map[key] = temp

        
        pages_mapping = [
            self.file_type_shp, self.file_type_txt, self.file_type_csv, self.file_type_json,
            self.file_type_geojson, self.file_type_fold, self.file_type_layer,
        ]
        self.pages_mapping_connect(self.output_by_file, pages_mapping, self.stackedWidget,  self.pathInfoWidget)

        
        tep_labels = [self.qlabel_8913, self.qlabel_4071, self.qlabel_8388, self.qlabel_2791]
        self.update_visible_step_labels(tep_labels)

        
        for btn in [
            self.file_type_shp, self.file_type_txt, self.file_type_csv,
            self.file_type_json, self.file_type_geojson, self.file_type_fold, self.file_type_layer
        ]:
            btn.toggled.connect(lambda _: self.update_visible_step_labels(tep_labels))

        
        self.fields = None
        self.set_duplicate_fields()

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

        
        self.fieldCheckButton.clicked.connect(lambda: self.check_duplicate_fields(self.fields, self.fieldName.text()))

        
        self.tablePushButton.clicked.connect(lambda: self.export_tablewidget_to_csv_with_dialog(self.tableResults))

        
        
        
        checked_index = next((i for i, btn in enumerate(pages_mapping) if btn.isChecked()), 0)
        self.stackedWidget.setCurrentIndex(checked_index)

    @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 pages_mapping_connect(self, output_by_file, buttons, stacked_widget, info):
        
        for index, button in enumerate(buttons):
            def handle_toggle(is_checked, page_index=index):
                if is_checked and output_by_file:
                    if page_index < stacked_widget.count():
                        stacked_widget.setCurrentIndex(page_index)
                        stacked_widget.setVisible(True)
                        info.setVisible(True)
                    else:
                        stacked_widget.setVisible(False)
                        info.setVisible(False)

            button.toggled.connect(handle_toggle)

    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 export_tablewidget_to_csv_with_dialog(self, table_widget):
        
        def is_tablewidget_all_cells_empty(table):
            return not any(table.item(r, c) and table.item(r, c).text().strip()
                           for r in range(table.rowCount())
                           for c in range(table.columnCount()))

        
        if is_tablewidget_all_cells_empty(table_widget):
            QMessageBox.warning(self, "값 없음", "테이블에 입력된 데이터가 없어 저장할 수 없습니다.")
            return

        
        file_path, _ = QFileDialog.getSaveFileName(
            None,
            "CSV 파일로 저장",
            "table_export.csv",
            "CSV Files (*.csv);;All Files (*)"
        )

        
        if not file_path:
            return

        
        try:
            with open(file_path, mode='w', newline='', encoding='utf-8-sig') as file:
                writer = csv.writer(file)
                
                headers = [table_widget.horizontalHeaderItem(col).text() for col in range(table_widget.columnCount())]
                writer.writerow(headers)

                
                for row in range(table_widget.rowCount()):
                    row_data = [
                        table_widget.item(row, col).text() if table_widget.item(row, col) else ''
                        for col in range(table_widget.columnCount())
                    ]
                    writer.writerow(row_data)

            QMessageBox.information(self, "성공", "CSV 파일로 저장되었습니다:")

        except Exception as e:
            QMessageBox.critical(self, "오류", f"저장 중 오류 발생:\n{str(e)}")

    def table_display(self, table, header, rows):
        
        try:
            table.setColumnCount(len(header))
            table.setRowCount(len(rows))
            table.setHorizontalHeaderLabels(header)

            for row_idx, row in enumerate(rows):
                for col_idx, value in enumerate(row):
                    table.setItem(row_idx, col_idx, QTableWidgetItem(str(value)))

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

    def set_duplicate_fields(self):
        

        def disable_radio_buttons(radio_buttons):
            
            for btn in radio_buttons:
                btn.setAutoExclusive(False)  
                btn.setChecked(False)  
                btn.setEnabled(False)  

        def enable_radio_buttons(radio_buttons):
            
            for btn in radio_buttons:
                btn.setAutoExclusive(True)  
                btn.setEnabled(True)  
                btn.setChecked(True)  

        def check_first_enabled_radiobutton(radio_buttons):
            
            for btn in radio_buttons:
                if btn.isEnabled():
                    btn.setChecked(True)
                    break

        def get_disabled_radiobuttons(radio_buttons):
            
            return [btn for btn in radio_buttons if not btn.isEnabled()]

        try:
            if common.fileInfo_1.file_preview:
                
                fields = [h for item in common.fileInfo_1.file_preview for h in item.header]
                self.fieldTextEdit.setText(',   '.join(fields))
                self.fields = fields

            if common.fileInfo_1.file_record:
                
                source_file_type, _, _ = common.fileInfo_1.file_record.get_record()
                
                disabled_radiobuttons = get_disabled_radiobuttons(self.disable_map['all'])
                
                
                if Counter(self.disable_map[source_file_type]) != Counter(disabled_radiobuttons):
                    
                    enable_radio_buttons(self.disable_map['all'])
                    
                    disable_radio_buttons(self.disable_map[source_file_type] if source_file_type else [])
                    
                    check_first_enabled_radiobutton(self.disable_map['all'])

            if common.fileInfo_1.result_table["header"] and common.fileInfo_1.result_table["rows"]:
                
                header = common.fileInfo_1.result_table["header"]
                rows = common.fileInfo_1.result_table["rows"]
                msg = common.fileInfo_1.result_table["msg"]

                self.table_display(self.tableResults, header, rows)
                self.statsResult.setText(msg)

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

    def check_duplicate_fields(self, fields, field_name):
        
        try:
            if self.validate_shp_field_name(fields, field_name):
                QMessageBox.warning(self, "필드명 유효성", "사용 가능한 필드명입니다.")

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

    def validate_shp_field_name(self, fields, field_name):
        
        try:
            
            if not field_name.strip():
                QMessageBox.warning(self, "필드명 오류", "필드명을 입력해 주세요.")
                return False

            
            if field_name != field_name.strip():
                QMessageBox.warning(self, "필드명 오류", "필드명의 앞뒤에는 공백을 포함할 수 없습니다.")
                return False

            
            if len(field_name) > 10:
                QMessageBox.warning(self, "필드명 오류", "필드명은 10자 이내여야 합니다.")
                return False

            
            if (
                    not re.match(r'^[A-Za-z가-힣_][A-Za-z0-9가-힣_]*$', field_name)
                    or len(field_name.encode("utf-8")) > 10
            ):
                QMessageBox.warning(
                    self,
                    "필드명 오류",
                    "필드명은 영문/한글 또는 밑줄(_)로 시작해야 하며,\n"
                    "영문자, 한글, 숫자, 밑줄(_)만 사용할 수 있습니다.\n"
                    "또한 Shapefile 규격상 필드명은 UTF-8 기준 10바이트 이내여야 합니다.\n\n"
                    "예) 한글 3글자 + 영문 1글자 = 10바이트"
                )
                return False

            
            if field_name in fields:
                QMessageBox.warning(self, "필드명 오류", "입력한 필드명은 이미 존재합니다.\n\n아래에 표시된 필드명은 현재 사용 중이며, 중복 사용이 불가능합니다.")
                return False

            return True

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

    def set_fileResults(self, num=1):
        
        try:
            result_path = None
            result_field = None

            
            for file_type in [
                self.input_mapping_shp,
                self.input_mapping_json,
                self.input_mapping_geojson,
                self.input_mapping_txt,
                self.input_mapping_csv,
                self.input_mapping_fold
            ]:
                
                if file_type["file_type"].isVisibleTo(self):

                    
                    if file_type["file_type"].isChecked():

                        result_path = file_type["file_widget"].filePath()
                        if not result_path:
                            QMessageBox.information(self, "결과 저장 경로 없음", "결과를 저장할 경로를 먼저 설정해 주세요.", QMessageBox.Ok)
                            return False

            
            if self.fieldName.isVisibleTo(self):
                result_field = self.fieldName.text()
                if not self.validate_shp_field_name(self.fields, result_field):
                    return False

            
            if self.output_by_file:

                
                if result_path is None:
                    
                    common.fileInfo_1.result_record.set_record('layer')

                
                elif bool(os.path.splitext(result_path)[1]):
                    common.fileInfo_1.result_record.set_record(result_path.split('.')[-1].lower(), result_path)

                
                else:
                    common.fileInfo_1.result_record.set_record('folder', result_path)

            
            if self.output_by_field:
                common.fileInfo_1.result_field = result_field

            

            return True

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

