from dataclasses import dataclass, field
from typing import Tuple
from classes.TW_Utility import TW_Utility

@dataclass
class TW_Object_Type_Validation:
    name:str
    mainType:type
    nullable:bool = False
    subType:type = None
    subSubType:type = None

@dataclass
class TW_Object_Type_Validator:
    propertiesType: list[TW_Object_Type_Validation] = field(default_factory=lambda:[])

    def _validateList(self, data:any, validationPropertyInfo:TW_Object_Type_Validation, level = 1, errors:list = None) -> Tuple[bool, list[str]]:
        if errors is None:
            errors = []
        
        typeToCheck = 'subType' if level == 1  else 'subSubType'
        
        if TW_Utility.empty_or_none(validationPropertyInfo, typeToCheck):
            return True, errors
    
        if type(data) != list:
            errors.append(f"wrong type {validationPropertyInfo.name} {typeToCheck}")
            return False, errors

        validatedType = TW_Utility.getattr(validationPropertyInfo, typeToCheck)
        if level < 2:
            dataToCheck= []
            for i in range(0,len(data)):
                if type(data[i]) != validatedType:
                    errors.append(f"wrong type {validationPropertyInfo.name} {typeToCheck}")
                    return False, errors
                else:
                    if type(data[i]) == list:
                        for j in range(0,len(data[i])):
                            dataToCheck.append(data[i][j])
                        break
                    else:
                        dataToCheck.append(data[i])
            
            if not TW_Utility.empty_or_none(validationPropertyInfo, 'subSubType'):
                level = level + 1
                return self._validateList(dataToCheck, validationPropertyInfo, level, errors)
            
        else:
            for i in range(0,len(data)):
                if type(data[i]) != validatedType:
                    errors.append(f"wrong type {validationPropertyInfo.name} {typeToCheck}")
                    return False, errors

        return True, errors
    
    def __appendToValidationError(self, validationErrors, name:str, message:str):
        if TW_Utility.empty_or_none(validationErrors, name):
            validationErrors[name] = []

        validationErrors[name].append(message)
        return validationErrors

    def validateType(self, objectToValidate:any):
        validationErrors = {}

        seenProperties = []
        for p in self.propertiesType:
            if len(TW_Utility.tw_where(seenProperties, lambda x:x == p.name)) > 0:
                continue

            seenProperties.append(p.name)

            propertiesDefinitionToValidate = [p]
            if len(TW_Utility.tw_where(self.propertiesType, lambda x:x.name == p.name)) > 1:
                propertiesDefinitionToValidate = TW_Utility.tw_where(self.propertiesType, lambda x:x.name == p.name)

            if len(TW_Utility.tw_where(propertiesDefinitionToValidate, lambda x:x.nullable == False)) > 0 and not TW_Utility.hasattr(objectToValidate, p.name):
                self.__appendToValidationError(validationErrors, p.name, f"{p.name} missing")
                continue

            if not TW_Utility.hasattr(objectToValidate, p.name):
                continue

            validatedValue = TW_Utility.getattr(objectToValidate, p.name)

            definitionErrors = []
            for item in propertiesDefinitionToValidate:
                if item.mainType is not (list) and type(validatedValue) != item.mainType:
                    definitionErrors.append(f"wrong type for{item.name} found {type(validatedValue)} expected {item.mainType}")
                elif item.mainType is list and self._validateList(validatedValue, item)[0] == False:
                    for err in self._validateList(validatedValue, item)[1]:
                        definitionErrors.append(err)

            if len(definitionErrors) == len(propertiesDefinitionToValidate):
                errorMessage = " and ".join(definitionErrors)
                self.__appendToValidationError(validationErrors, p.name, errorMessage)

        return validationErrors
            
    