"""
/***************************************************************************
                                 A QGIS plugin
 Набор инструментов для работы с материалами документации территориального планирования
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2024-03-18
        git sha              : $Format:%H$
        copyright            : (C) 2024 by I.Bukharin/Mirproekt
        email                : iabukharin@gmail.com
 ***************************************************************************/
 Функции применения стилей Приказа 10 к слоям
"""

from qgis.core import (QgsDefaultValue, 
                       QgsEditorWidgetSetup, 
                       QgsFieldConstraints, 
                       QgsApplication, 
                       QgsSymbol, 
                       QgsSymbolLayer, 
                       QgsProperty, 
                       QgsRendererCategory, 
                       QgsCategorizedSymbolRenderer, 
                       QgsMapLayer, NULL, QgsConditionalStyle)
from qgis.PyQt.QtGui import QColor
from os.path import isfile
from os.path import join as joinpath

def delChildrenRule(layer, rule, filter):
    if filter is not None or rule.filter() is not None:
        filtersList = [f.expression() for f in [filter, rule.filter()] if f is not None]
        layer.selectByExpression(' AND '.join(filtersList))
        if layer.selectedFeatureCount() < 1:
            return True
    for child in rule.children():
        filter = rule.filter()
        if delChildrenRule(layer, child, filter):
            rule.removeChild(child)
    return False

def delNullSymbols(layer):
    cur_renderer = layer.renderer()
    cur_rendererType = cur_renderer.type()
    # print(cur_renderer.usedAttributes())
    if cur_rendererType == 'categorizedSymbol':
        categoryCondition = cur_renderer.classAttribute()
        attributeFlag = False
        if categoryCondition in layer.fields().names():
            indxFieldClassid = layer.fields().indexOf(categoryCondition)
            presentUniqValues = list(layer.uniqueValues(indxFieldClassid))
            attributeFlag = True
        for indx in range(len(cur_renderer.categories()) - 1, -1, -1):
            category = cur_renderer.categories()[indx]
            valuesForCategory = category.value()
            #print(valuesForCategory)
            if valuesForCategory is not None and valuesForCategory != '':
                if not isinstance(valuesForCategory, list): valuesForCategory = [valuesForCategory,]
                selCount = 0
                for valueForCategory in valuesForCategory:
                    layer.selectByExpression(f'{categoryCondition}={valueForCategory}')
                    selCount = layer.selectedFeatureCount()
                    layer.selectByExpression(f"to_string({categoryCondition})='{valueForCategory}'")
                    selCount += layer.selectedFeatureCount()
                if selCount == 0:
                    cur_renderer.deleteCategory(indx)
                    """ if attributeFlag:
                        if valueForCategory not in presentUniqValues:
                            cur_renderer.deleteCategory(indx)
                    else:
                        layer.selectByExpression(f'{categoryCondition}={valueForCategory}')
                        selCount = layer.selectedFeatureCount()
                        layer.selectByExpression(f"{categoryCondition}='{valueForCategory}'")
                        selCount += layer.selectedFeatureCount()
                        if selCount == 0:
                            cur_renderer.deleteCategory(indx) """
    elif cur_rendererType == 'RuleRenderer':
        rootRule =cur_renderer.rootRule()
        delChildrenRule(layer, rootRule, rootRule.filter())
    return

def applyStyleSymbols(order10layer, simpleTypes, complexTypes, gifPath, layerComplexType=''):
    try:
        layerGeomType = order10layer.geometryType()
    except Exception:
        return
    
    classDict = {}
    for key, val in (simpleTypes or {}).items():
        if 'CLASSID' in key and isinstance(val, dict):
            dict_values = val.get('dictValues', {})
            for k, v in dict_values.items():
                classDict[str(k)] = str(v)

    if '_Type' in layerComplexType and layerComplexType in complexTypes.keys():
        complexType = complexTypes[layerComplexType]
        simpleTypeName = complexType['attributes']['CLASSID']['baseType'] # fgistp:Prison_CLASSID
        simpleType = simpleTypes.get(simpleTypeName, simpleTypes.get(simpleTypeName.replace('fgistp:', ''), ''))
        if simpleType == '':
            complexType = ''
    else:
        complexType = ''
    flagClassid = False
    flagStatus = False
    flagStatusAdm = False
    flagRegStatus = False
    if 'CLASSID' in order10layer.fields().names():
        flagClassid = True
    if 'STATUS' in order10layer.fields().names():
        flagStatus = True
    elif 'STATUS_ADM' in order10layer.fields().names():
        flagStatusAdm = True
    if 'REG_STATUS' in order10layer.fields().names():
        flagRegStatus = True
        
    if layerGeomType == 0:
        gifPath = gifPath + '/UslZn/602050304.gif'
        categorysValues = []
        if not flagClassid:
            return
        if complexType == '':
            indxFieldClassid = order10layer.fields().indexOf('CLASSID')
            categorysValues = list(order10layer.uniqueValues(indxFieldClassid))
        else:
            categorysValues = list(simpleType['dictValues'].keys())
        if len(categorysValues) < 1:
            return
        simpleMarkerLayer = QgsApplication.symbolLayerRegistry().symbolLayerMetadata("SimpleMarker")
        rasterMarkerLayer = QgsApplication.symbolLayerRegistry().symbolLayerMetadata("RasterMarker")
        def_symb_template = [
                {'angle': '0', 'color': '255,255,255,0', 'horizontal_anchor_point': '1', 'joinstyle': 'round', 'name': 'rounded_square', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '0,0,0,0', 'outline_style': 'solid', 'outline_width': '0.3', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '8', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'},
                {'angle': '0', 'color': '255,255,255,0', 'horizontal_anchor_point': '1', 'joinstyle': 'round', 'name': 'rounded_square', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '0,0,0,0', 'outline_style': 'solid', 'outline_width': '0.3', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '7', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'},
                {'angle': '0', 'color': '255,255,255,255', 'horizontal_anchor_point': '1', 'joinstyle': 'round', 'name': 'rounded_square', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '0,0,0,255', 'outline_style': 'solid', 'outline_width': '0.3', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '6', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'},
                {'alpha': '1', 'angle': '0', 'fixedAspectRatio': '0', 'horizontal_anchor_point': '1', 'imageFile': gifPath, 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'Pixel', 'scale_method': 'diameter', 'size': '8.3', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'},
                {'angle': '45', 'color': '255,0,0,255', 'horizontal_anchor_point': '1', 'joinstyle': 'bevel', 'name': 'line', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '255,0,0,0', 'outline_style': 'solid', 'outline_width': '0.4', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '8.8', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'}
                ]
        def_default_template = [
                {'angle': '0', 'color': '219,30,42,255', 'horizontal_anchor_point': '1', 'joinstyle': 'bevel', 'name': 'circle', 'offset': '0,0', 'offset_map_unit_scale': '3x:0,0,0,0,0,0', 'offset_unit': 'MM', 'outline_color': '128,17,25,255', 'outline_style': 'solid', 'outline_width': '0.4', 'outline_width_map_unit_scale': '3x:0,0,0,0,0,0', 'outline_width_unit': 'MM', 'scale_method': 'diameter', 'size': '6', 'size_map_unit_scale': '3x:0,0,0,0,0,0', 'size_unit': 'MM', 'vertical_anchor_point': '1'}
                ]
        categories = []
        for categoryValue in categorysValues:
            symbol = QgsSymbol.defaultSymbol(layerGeomType)
            for ind, symb in enumerate(symbol.symbolLayers()):
                symbol.deleteSymbolLayer(ind)
            if flagRegStatus:
                # федерального значения
                symbol.appendSymbolLayer(simpleMarkerLayer.createSymbolLayer(def_symb_template[0]))
                if flagStatus:
                    symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyFillColor, QgsProperty.fromExpression('color_rgba(255,if("STATUS"=2, 0, 255),if("STATUS"=2 or "STATUS"=3, 0, 255),if(  "REG_STATUS" <2, 255,0))') )
                else:
                    symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyFillColor, QgsProperty.fromExpression('color_rgba(255,255,255,if(  "REG_STATUS" <2, 255,0))') )
                symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyStrokeColor, QgsProperty.fromExpression('color_rgba( 0, 0, 0, if("REG_STATUS" = 1, 255,0))'))
                # регионального значения
                symbol.appendSymbolLayer(simpleMarkerLayer.createSymbolLayer(def_symb_template[1]))
                if flagStatus:
                    symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyFillColor, QgsProperty.fromExpression('color_rgba( 255,if("STATUS"=2, 0, 255),if("STATUS"=2 or "STATUS"=3, 0, 255),if(  "REG_STATUS" <3, 255,0))') )
                else:
                    symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyFillColor, QgsProperty.fromExpression('color_rgba( 255,255,255,if(  "REG_STATUS" <3, 255,0))') )
                symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyStrokeColor, QgsProperty.fromExpression('color_rgba( 0, 0, 0, if(  "REG_STATUS" <3, 255,0))'))
            # местного значения
            symbol.appendSymbolLayer(simpleMarkerLayer.createSymbolLayer(def_symb_template[2]))
            symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyFillColor, QgsProperty.fromExpression('color_rgba( 255,if("STATUS"=2, 0, 255),if("STATUS"=2 or "STATUS"=3, 0, 255),255)') )
            # иконка
            i_template = def_symb_template[3].copy()
            i_template['imageFile']=i_template['imageFile'].replace('602050304.gif', categoryValue+'.gif')
            if not isfile( i_template['imageFile']):
                continue
            symbol.appendSymbolLayer(rasterMarkerLayer.createSymbolLayer(i_template))
            # ликвидируемый
            if flagStatus:
                symbol.appendSymbolLayer(simpleMarkerLayer.createSymbolLayer(def_symb_template[4]))
                symbol.symbolLayer(len(symbol.symbolLayers()) - 1).setDataDefinedProperty(QgsSymbolLayer.PropertyStrokeColor, QgsProperty.fromExpression('color_rgba( 255, 0, 0, if(  "STATUS" =4, 255,0))'))

            categories.append(QgsRendererCategory(categoryValue, symbol, classDict[categoryValue]))
        
        symbol = QgsSymbol.defaultSymbol(layerGeomType)
        symbol.deleteSymbolLayer(0)
        symbol.appendSymbolLayer(simpleMarkerLayer.createSymbolLayer(def_default_template[0]))
        categories.append(QgsRendererCategory(None, symbol,''))
        renderer = QgsCategorizedSymbolRenderer('CLASSID', categories)
        order10layer.setRenderer(renderer)
        order10layer.triggerRepaint()
        # 
    else:
        qmlPath = joinpath(gifPath+'/QML/', 'ST_'+layerComplexType.replace('_Type', '')+'.qml')
        if layerGeomType == 1 and isfile(joinpath(gifPath+'/QML/', 'ST_'+layerComplexType.replace('_Type', '')+'_multilinestring'+'.qml')):
            qmlPath = joinpath(gifPath+'/QML/', 'ST_'+layerComplexType.replace('_Type', '')+'_multilinestring'+'.qml')
        if isfile(qmlPath):
            order10layer.loadNamedStyle(qmlPath, loadFromLocalDb=True, categories = QgsMapLayer.Symbology)
            order10layer.triggerRepaint()
    return

def applyFieldConstrainsOrder10(order10layer, simpleTypes, complexTypes, layerComplexType=''):
    if '_Type' in layerComplexType and layerComplexType in complexTypes.keys():
        complexType = complexTypes[layerComplexType]
    else:
        complexType = {'attributes':{}}

    layerConditStyles = order10layer.conditionalStyles()

    layer_form_config = order10layer.editFormConfig()

    fields = order10layer.fields()

    for field in fields:
        field_name = field.name()
        fieldIndex = order10layer.fields().indexFromName(field_name)

        Field_dict = []
        field_values = []

        if field_name in complexType['attributes'].keys():
            field.setComment(complexType['attributes'][field_name].get('typeUserName', ''))

        if field_name == 'GLOBALID':
            defValue = QgsDefaultValue('substr(uuid(), 2, 36)')
            defValue.setApplyOnUpdate(False)
            order10layer.setDefaultValueDefinition(fieldIndex, defValue)
            continue

        # Вспомогательная функция для заполнения справочников
        def fill_dict_from_simple_type(type_key):
            if type_key in simpleTypes:
                dict_vals = simpleTypes[type_key].get('dictValues', {})
                if isinstance(dict_vals, dict) and len(dict_vals) > 0:
                    for val, alias in dict_vals.items():
                        Field_dict.append({str(alias): str(val)})
                        field_values.append(str(val))
                    Field_dict.append({'<NULL>': '{2839923C-8B7D-419E-B84B-CA2FE9B80EC7}'})
                    return True
            return False

        found_map = False
        if field_name in simpleTypes:
            found_map = fill_dict_from_simple_type(field_name)
        elif field_name in complexType['attributes']:
            base_type = complexType['attributes'][field_name].get('baseType', '').replace('fgistp:', '')
            found_map = fill_dict_from_simple_type(base_type)
        
        if not found_map and (field_name + '_Type') in simpleTypes:
            found_map = fill_dict_from_simple_type(field_name + '_Type')
            
        if not found_map and field_name == 'CLASSID' and '_Type' in layerComplexType:
            class_id_type = layerComplexType.replace('_Type', '') + '_CLASSID'
            found_map = fill_dict_from_simple_type(class_id_type)

        # Применение виджета ValueMap
        if found_map:
            # allow_null=True уже встроено в логику виджета, UUID не обязателен
            setup = QgsEditorWidgetSetup('ValueMap', {'map': Field_dict, 'allow_null': True})
            order10layer.setEditorWidgetSetup(fieldIndex, setup)
                
        if complexType != '' and field_name in complexType['attributes'].keys():
            
            layer_form_config.setReuseLastValue(fieldIndex, True)

            attr_data = complexType['attributes'][field_name]
            mirDepend = attr_data.get('mirDepend', '')
            minOccurs = attr_data.get('minOccurs', 'N')

            order10layer.setEditFormConfig(layer_form_config)

            if len(mirDepend) > 1 and '=' in mirDepend:
                # Безопасный парсинг STATUS=[2, 3]
                try:
                    dep_field = mirDepend.split('=')[0].strip()
                    dep_values = mirDepend[mirDepend.find('[')+1 : mirDepend.find(']')]
                    
                    val_list_str = ",".join([f"'{x.strip()}'" for x in field_values])
                    
                    if field_values:
                        constrainStr = f'CASE WHEN "{dep_field}" IN ({dep_values}) THEN ("{field_name}" IS NOT NULL AND "{field_name}" IN ({val_list_str})) ELSE "{field_name}" IS NULL END'
                        extra_style = f"OR to_string(attribute(@feature, '{field_name}')) NOT IN ({val_list_str})"
                    else:
                        constrainStr = f'CASE WHEN "{dep_field}" IN ({dep_values}) THEN "{field_name}" IS NOT NULL ELSE TRUE END'
                        extra_style = ""

                    order10layer.setFieldConstraint(fieldIndex, QgsFieldConstraints.ConstraintExpression, QgsFieldConstraints.ConstraintStrengthSoft)
                    order10layer.setConstraintExpression(fieldIndex, constrainStr)

                    # Условный стиль (оранжевый фон при ошибке)
                    cond_expr = f"(((@value IS NULL OR @value = '') {extra_style}) AND to_string(attribute(@feature, '{dep_field}')) IN ({dep_values})) OR ((@value > 0) AND to_string(attribute(@feature, '{dep_field}')) NOT IN ({dep_values}))"
                    style = QgsConditionalStyle(cond_expr)
                    style.setBackgroundColor(QColor(255, 162, 0))
                    layerConditStyles.setFieldStyles(field_name, [style])
                except:
                    pass # Пропуск при ошибке парсинга mirDepend

            # Простая обязательность (minOccurs = Y)
            elif minOccurs == 'Y':
                val_list_str = ",".join([f"'{x.strip()}'" for x in field_values])
                constrainStr = f'"{field_name}" IS NOT NULL'
                if field_values:
                    constrainStr += f' AND "{field_name}" IN ({val_list_str})'

                order10layer.setFieldConstraint(fieldIndex, QgsFieldConstraints.ConstraintExpression, QgsFieldConstraints.ConstraintStrengthSoft)
                order10layer.setConstraintExpression(fieldIndex, constrainStr)
                
                style = QgsConditionalStyle("@value IS NULL OR @value = ''")
                style.setBackgroundColor(QColor(255, 162, 0))
                layerConditStyles.setFieldStyles(field_name, [style])

    # Сохраняем конфиг формы один раз после цикла
    order10layer.setEditFormConfig(layer_form_config)
    return