"""
/***************************************************************************
                                 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
 ***************************************************************************/
 Функции проверки слоев
"""

from qgis.PyQt.QtCore import QVariant
import re
from copy import deepcopy

def checkAttrValueForNull2(valueForCheck, field):
    """
    Проверяет на отсутствие значения атрибута

    Args:
        valueForCheck (any): значение атрибута
        field (QgsField): атрибут
    
    Returns:
        bool: True если значение атрибутна отсутсвует
    """

    nullFlag = False
    featureFieldType = field.type()
    if valueForCheck is None:
        nullFlag = True
    elif QVariant(valueForCheck).isNull():
        nullFlag = True
    elif str(valueForCheck) in ['NULL', 'Null']:
        nullFlag = True
    elif featureFieldType == QVariant.String:
        if valueForCheck == '' or valueForCheck in ['NULL', 'Null', ' ', 'None', 'null']:
            nullFlag = True
    else:
        if valueForCheck == 0:
            nullFlag = True
        if valueForCheck == 0.0:
            nullFlag = True
    return nullFlag

def checkLayerOnFields(layerForCheck, complexTypes, simpleTypesIn, layerClassTypeName, onlyReqField=True):
    simpleTypes = deepcopy(simpleTypesIn)
    fieldsReport = ''
    layerFields = layerForCheck.fields()
    layerFieldsNames = layerForCheck.fields().names()
    fieldsReport += f'<span style="font-size:9pt;">Состав атрибутов . слоя: {layerFieldsNames}</span><br />'
    fieldsReport += f'<span style="font-size:9pt;">Состав атрибутов класса: {list(complexTypes[layerClassTypeName]["attributes"].keys())}</span><br />'
    etalonFieldDict = dict()
    for etalonFieldName, dictValue in complexTypes[layerClassTypeName]['attributes'].items():
        if 'xs:' in dictValue['baseType']:
            etalonFieldType = dictValue['baseType'].lower()
        else:
            etalonFieldType = simpleTypes[dictValue['baseType'].replace('fgistp:', '')]['baseType'].lower()
        if 'integer' in etalonFieldType:
            etalonFieldDict[etalonFieldName] = ['integer', True, False]
        elif 'string' in etalonFieldType:
            etalonFieldDict[etalonFieldName] = ['string', True, False]
        elif 'double' in etalonFieldType:
            etalonFieldDict[etalonFieldName] = ['double', True, False]

        if dictValue['minOccurs'] == 'N':
            etalonFieldDict[etalonFieldName][1] = False
        if dictValue['minOccurs'] == 'C' and len(dictValue['mirDepend']) > 1:
            etalonFieldDict[etalonFieldName][2] = True
    extraLayerFieldNames = []
    for field in layerFields:
        fieldName = field.name()
        fieldType = field.type()
        if fieldType == QVariant.Int:
            fieldType = 'integer'
        elif fieldType == QVariant.String:
            fieldType = 'string'
        elif fieldType == QVariant.Double:
            fieldType = 'double'
        typeFlag = False
        nameFlag = False
        reqFlag = False
        if fieldName in list(etalonFieldDict.keys()):
            nameFlag = True
            if fieldType == etalonFieldDict[fieldName][0]:
                typeFlag = True
            if etalonFieldDict[fieldName][1]:
                reqFlag = True
            typeFieldClass = etalonFieldDict[fieldName][0]
            del etalonFieldDict[fieldName]
        if not nameFlag:
            extraLayerFieldNames.append(fieldName)
        if not reqFlag and onlyReqField:
            continue
        if not typeFlag and fieldName not in extraLayerFieldNames:
            fieldsReport += f'<span style="color:red;font-size:11pt;"><b>{fieldName}</b> тип поля слоя {fieldType} не соответсвует типу поля класс {typeFieldClass} </span><br />'
    for etalonFieldName in etalonFieldDict:
        if onlyReqField and not etalonFieldDict[etalonFieldName][1]:
            continue
        if etalonFieldDict[etalonFieldName][1]:
            if etalonFieldDict[etalonFieldName][2]:
                fieldsReport += f'<span style="color:orange;font-size:11pt;"><b>{etalonFieldName}</b> отсутсвует условно-обязательное поле </span><br />'
            else:
                fieldsReport += f'<span style="color:red;font-size:11pt;"><b>{etalonFieldName}</b> отсутсвует обязательное поле </span><br />'
        else:
            fieldsReport += f'<span style="color:orange;font-size:10pt;"><b>{etalonFieldName}</b> отсутсвует не обязательное поле </span><br />'
    if not onlyReqField and len(extraLayerFieldNames) > 0:
        fieldsReport += f'<span style="color:orange;font-size:10pt;">слой содержит "лишние" поля: {extraLayerFieldNames}</span><br />'

    return fieldsReport

def checkFeatures(layerForCheck, complexTypes, simpleTypes, layerClassTypeName, onlyReqField=True):
    featureReportOut = ''
    uuidPatterns = '(urn:uuid:)?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}'
    etalonFields = deepcopy(complexTypes[layerClassTypeName]['attributes'])

    for etalonField in etalonFields:
        etalonFields[etalonField]['dictValues'] = []
        if 'fgistp:' in etalonFields[etalonField]['baseType']:
            etalonFields[etalonField]['dictValues'] = simpleTypes[etalonFields[etalonField]['baseType'].replace('fgistp:', '')].get('dictValues', [])
            etalonFields[etalonField]['baseType'] = simpleTypes[etalonFields[etalonField]['baseType'].replace('fgistp:', '')]["baseType"]
            
        etalonFields[etalonField]['baseType'] = etalonFields[etalonField]['baseType'].replace('xs:', '')
        if etalonFields[etalonField]['baseType'].lower() == 'string':
            etalonFields[etalonField]['baseType'] = QVariant.String
        elif etalonFields[etalonField]['baseType'].lower() == 'double':
            etalonFields[etalonField]['baseType'] = QVariant.Double
        else:
            etalonFields[etalonField]['baseType'] = QVariant.Int

    for featForCheck in layerForCheck.getFeatures():
        featureReport = ''
        featureID = featForCheck.id()
        featureFields = featForCheck.fields()
        featGLOBALID = ''

        if 'GLOBALID' in etalonFields.keys() and 'GLOBALID' in featureFields.names():
            fieldID = featForCheck.fieldNameIndex('GLOBALID')
            
            featGLOBALID = featForCheck['GLOBALID']
            if checkAttrValueForNull2(featGLOBALID, featureFields['GLOBALID']):
                featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> значение атрибута <b>GLOBALID</b> не соответсвует требованию к UUID</span><br />'
            else:
                forCheckUUUID = re.search(uuidPatterns, featGLOBALID)
                if forCheckUUUID is not None:
                    forCheckUUUID = forCheckUUUID.span()
                else:
                    forCheckUUUID = (0, 100)
                if forCheckUUUID[0] != 0 or forCheckUUUID[1] != len(featGLOBALID):
                    featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> значение атрибута <b>GLOBALID</b> не соответсвует требованию к UUID</span><br />'
        
        if featForCheck.hasGeometry():
            featureGeom = featForCheck.geometry()
            featureGeomType = featureGeom.wkbType()
        else:
            featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> не содержит геометрию</span><br />'

        for featureField in featureFields:
            
            featureFieldName = featureField.name()
            featureFieldType = featureField.type()
            if featureFieldName == 'GLOBALID':
                continue
            etalonField = etalonFields.get(featureFieldName, 'не найден')
            
            if etalonField == 'не найден':
                continue
            elif onlyReqField and etalonField['minOccurs'] == 'N':
                continue
            else:
                featFieldValue = featForCheck[featureFieldName]
                featFieldValueNullFlag = False
                mirDepend = []
                mirDependField = ''
                mirDependValueList = []
                mirDependValueString = ''
                if etalonField['minOccurs'] == 'C' or len(etalonField['mirDepend']) > 1:
                    mirDepend = etalonFields[featureFieldName]['mirDepend']
                    mirDependField = mirDepend[:mirDepend.find('=')]
                    mirDependValueList = [int(x) for x in mirDepend[mirDepend.find('=')+2:-1].split(',')]
                    mirDependValueString = mirDepend[mirDepend.find('=')+2:-1]
                
                """ if featFieldValue is None:
                    featFieldValueNullFlag = True
                elif featureFieldType == QVariant.String:
                    if featFieldValue == '' or featFieldValue in ['NULL', 'Null', ' ', '']:
                        featFieldValueNullFlag = True
                else:
                    if featFieldValue == 0:
                        featFieldValueNullFlag = True
                    if featFieldValue == 0.0:
                        featFieldValueNullFlag = True """
                featFieldValueNullFlag = checkAttrValueForNull2(featFieldValue, featureField)
                
                if featFieldValueNullFlag:
                    if etalonField['minOccurs'] == 'Y' and len(etalonField['mirDepend']) < 1:
                        featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> не заполнено обязательное поле <b>{featureFieldName}</b></span><br />'
                        continue
                    elif etalonField['minOccurs'] == 'N':
                        featureReport += f'<span style="color:orange;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> не заполнено не обязательное поле <b>{featureFieldName}</b></span><br />'
                        continue
                    else:
                        if featForCheck[mirDependField] in mirDependValueList or str(featForCheck[mirDependField]) in [str(x) for x in mirDependValueList]:
                            #print(f'{featForCheck[mirDependField]} in {mirDependValueList} or {str(featForCheck[mirDependField])} in {[str(x) for x in mirDependValueList]}')
                            featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> не заполнено условно-обязательное поле <b>{featureFieldName}</b> для {mirDependField} = {featForCheck[mirDependField]}</span><br />'
                        continue
                if len(etalonField['dictValues']) > 0:
                    valuesList = list(etalonField['dictValues'].keys())
                    if featFieldValue not in valuesList and str(featFieldValue) not in [str(x) for x in valuesList]:
                        if etalonField['minOccurs'] == 'Y':
                            featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> значение обязательного поля <b>{featureFieldName}</b> не соответствует справочнику</span><br />'
                            continue
                        elif etalonField['minOccurs'] == 'N':
                            featureReport += f'<span style="color:orange;font-size:10pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> значение не обязательного поля <b>{featureFieldName}</b> не соответствует справочнику</span><br />'
                            continue
                        else:
                            if featForCheck[mirDependField] not in mirDependValueList and not featFieldValueNullFlag:
                                featureReport += f'<span style="color:red;font-size:11pt;">Объект fid: <b>{featureID}</b> GLOBALID: <b>{featGLOBALID}</b> не верно заполнено условно-обязательное поле <b>{featureFieldName}</b> для {mirDependField} = {featForCheck[mirDependField]}</span><br />'
                            continue
        if len(featureReport) > 0:
            featureReportOut += f'<span style="font-size:10pt;"><b>----------------</b></span><br />'
            featureReportOut += featureReport

    return featureReportOut