from youtube_autonomous.segments.builder.ai.enums import VoiceEngine
from youtube_autonomous.segments.enums import SegmentField, EnhancementField, ProjectStatus, SegmentStatus
from youtube_autonomous.shortcodes.objects.shortcode_tag import YTAShortcodeTag
from youtube_autonomous.elements.builder.premade_element_builder import PremadeElementBuilder
from youtube_autonomous.elements.builder.effect_element_builder import EffectElementBuilder
from youtube_autonomous.elements.builder.text_element_builder import TextElementBuilder
from youtube_autonomous.elements.validator.element_validator import ElementValidator
from youtube_autonomous.elements.rules.element_rules import ElementRules
from youtube_autonomous.segments.enums import SegmentType, EnhancementType
from youtube_autonomous.elements.rules.rules_checker import RulesChecker
from youtube_autonomous.elements.validator.element_parameter_validator import ElementParameterValidator
from youtube_autonomous.segments.enums import StringDuration
from yta_shortcodes.tag_type import ShortcodeTagType
from yta_shortcodes.parser import ShortcodeParser
from yta_general_utils.logger import print_in_progress, print_error
from yta_general_utils.constants import Language
from yta_general_utils.programming.parameter_validator import PythonValidator
from datetime import datetime
from dataclasses import dataclass
from bson.objectid import ObjectId

import copy
import json


@dataclass
class VoiceNarration:
    """
    @dataclass
    The information we need to have to be able
    to build a voice narration.

    A segment can be or can be not narrated. If
    narrated, the narration can be built on 
    demand or can be taken from a given audio
    narration file. So, there are 3 valid
    scenarios:

    1. VoiceNarration is None, no narration
    needed.
    2. The 'filename' attribute is fulfilled, so
    it will be used as narration (if existing) 
    or will raise an Exception if not.
    3. The 'text' is fulfilled. All the other
    values are optional, but if provided they 
    must be valid.
    """

    text: str
    language: str
    speed: float
    engine: str
    voice: str
    filename: str

    # TODO: Move this
    
    DEFAULT_ENGINE = 'google'

    def is_valid(self):
        # 2. The 'filename' exist and is a valid audio file.
        if not hasattr(self, 'filename'):
            from yta_general_utils.file.checker import FileValidator

            if not FileValidator.file_is_audio_file(self.filename):
                raise Exception('The given "filename" {filename} is not a valid audio file.')
            
            # TODO: Maybe check if the audio is a narration so we
            # can get some audio transcription (?)

        # 3. The 'text' exist (and maybe the others).
        elif not hasattr(self, 'text'):
            if self.engine is None: self.engine = VoiceEngine.get_default().value
            else: VoiceEngine.to_enum(self.engine)

            if self.language is None: self.language = Language.SPANISH
            else: Language.to_enum(self.language)

            # TODO: Create a new strategy for the speed
            if self.speed is None: self.speed = 1
            else: self.speed = 1

            if self.voice is None: self.voice = VoiceEngine.to_enum(self.engine).get_default_voice()

# TODO: Should I move this class below to the
# enhancements module (?)
@dataclass
class Enhancement:
    """
    @dataclass
    The information an Enhancement must hold.
    This information is about a segment that
    is being processed, so its values can be
    different from the original json because
    they have been processed.
    """

    type: str
    url: str
    text: str
    text_to_narrate: str
    voice: str
    keywords: list[str]
    filename: str
    audio_narration_filename: str
    music: str

    extra_params: dict
    text_to_narrate_sanitized_without_shortcodes: str
    text_to_narrate_with_simplified_shortcodes: str
    text_to_narrate_sanitized: str
    duration: float
    status: str
    created_at: str

    def __init__(self):
        """
        Initializes the instance with empty string
        values for all the attributes so we can
        fill them later and individually when
        processing.
        """
        self.type = ''
        self.url = ''
        self.text = ''
        self.text_to_narrate = ''
        self.voice = ''
        self.keywords = []
        self.filename = ''
        self.audio_narration_filename = ''
        self.music = ''
        self.extra_params = {}
        self.text_to_narrate_sanitized_without_shortcodes = ''
        self.text_to_narrate_with_simplified_shortcodes = ''
        self.text_to_narrate_sanitized = ''
        self.duration = 0.0
        self.status = ''
        self.created_at = ''

    @property
    def as_dict(self):
        """
        Get the instance as a dictionary.
        """
        return {
            'type': self.type,
            'url': self.url,
            'text': self.text,
            'text_to_narrate': self.text_to_narrate,
            'voice': self.voice,
            'keywords': self.keywords,
            'filename': self.filename,
            'audio_narration_filename': self.audio_narration_filename,
            'music': self.music,
            'extra_params': self.extra_params,
            'text_to_narrate_sanitized_without_shortcodes': self.text_to_narrate_sanitized_without_shortcodes,
            'text_to_narrate_with_simplified_shortcodes': self.text_to_narrate_with_simplified_shortcodes,
            'text_to_narrate_sanitized': self.text_to_narrate_sanitized,
            'duration': self.duration,
            'status': self.status,
            'created_at': self.created_at
        }

    @property
    def as_json(self):
        """
        Get the instance as a json, transformed
        from the dictionary version.
        """
        return json.dumps(self.as_dict, ensure_ascii = False)

@dataclass
class Segment:
    """
    @dataclass
    The information a Segment must hold. This
    information is about a segment that is being
    processed, so its values can be different
    from the original json because they have 
    been processed.
    """

    type: str
    url: str
    text: str
    text_to_narrate: str
    voice: str
    keywords: list[str]
    filename: str
    audio_narration_filename: str
    music: str

    enhancements: list[Enhancement]
    extra_params: dict
    text_to_narrate_sanitized_without_shortcodes: str
    text_to_narrate_with_simplified_shortcodes: str
    text_to_narrate_sanitized: str
    duration: float
    status: str
    created_at: str

    def __init__(self):
        """
        Initializes the instance with empty string
        values for all the attributes so we can
        fill them later and individually when
        processing.
        """
        self.type = ''
        self.url = ''
        self.text = ''
        self.text_to_narrate = ''
        self.voice = ''
        self.keywords = []
        self.filename = ''
        self.audio_narration_filename = ''
        self.music = ''
        self.enhancements = []
        self.extra_params = {}
        self.text_to_narrate_sanitized_without_shortcodes = ''
        self.text_to_narrate_with_simplified_shortcodes = ''
        self.text_to_narrate_sanitized = ''
        self.duration = 0.0
        self.status = ''
        self.created_at = ''

    @property
    def as_dict(self):
        """
        Get the instance as a dictionary.
        """
        return {
            'type': self.type,
            'url': self.url,
            'text': self.text,
            'text_to_narrate': self.text_to_narrate,
            'voice': self.voice,
            'keywords': self.keywords,
            'filename': self.filename,
            'audio_narration_filename': self.audio_narration_filename,
            'music': self.music,
            'enhancements': [
                enhancement.as_dict
                for enhancement in self.enhancements
            ],
            'extra_params': self.extra_params,
            'text_to_narrate_sanitized_without_shortcodes': self.text_to_narrate_sanitized_without_shortcodes,
            'text_to_narrate_with_simplified_shortcodes': self.text_to_narrate_with_simplified_shortcodes,
            'text_to_narrate_sanitized': self.text_to_narrate_sanitized,
            'duration': self.duration,
            'status': self.status,
            'created_at': self.created_at
        }

    @property
    def as_json(self):
        """
        Get the instance as a json, transformed
        from the dictionary version.
        """
        return json.dumps(self.as_dict, ensure_ascii = False)

@dataclass
class MongoDBProject:
    """
    @dataclass
    The representation of a project in the mongo
    database, with its '_id' ObjectId and that
    stuff.

    This is what getting a project result from 
    the database must return.
    """

    id: ObjectId
    status: str
    script: str
    segments: str

    def __init__(
        self,
        json: dict
    ):
        self.id = json['_id']
        self.status = json['status']
        self.script = json['script']
        self.segments = json['segments']

        KEYS_TO_AVOID = ['_id', 'status', 'script', 'created_at']

        # Dynamically fulfill any other field
        for key, value in json.items():
            if key not in KEYS_TO_AVOID:
                setattr(self, key, value)

@dataclass
class ProjectForDatabase:
    """
    @dataclass
    A project which information has been transformed
    from the raw json to the information we need to
    be stored in the database.
    """

    status: str
    script: dict
    segments: list[Segment]

    @property
    def as_dict(self):
        """
        Get the instance as a dictionary.
        """
        return {
            'status': self.status,
            'script': self.script,
            'segments': [
                segment.as_dict
                for segment in self.segments
            ]
        }
    
    @property
    def as_json(self):
        """
        Get the instance as a json, transformed
        from the dictionary version.
        """
        return json.dumps(self.as_dict, ensure_ascii = False)

@dataclass
class ProjectAsJson:
    """
    @dataclass
    Class to represent the information of a initial
    project that is written as a json and also to
    validate this raw information.
    """

    json: dict
    """
    The raw json read from the file. Having this
    attribute accessible means it is valid because
    it has been validated when instantiating the
    dataclass.
    """

    def __init__(
        self,
        json: dict
    ):
        # Validate json and raise Exception if invalid
        ProjectAsJson.validate_project_json(json)
        
        # TODO: Continue and transform it
        self.json = json

    @property
    def for_database(
        self
    ) -> ProjectForDatabase:
        """
        TODO: Please, rename if possible

        Transform the project data from a raw json to a
        json that can be stored in the database (so some
        of its values have been updated properly).

        TODO: Previous comment below

        This method prepares the project data that has been previously
        verified so it has the structure with it will be stored in the
        database.

        This method will parse the 'text_to_narrate' and generate the
        some processed and sanitized fields (it processes the 
        shortcodes). It will also transform any string type duration
        to its actual numeric value that will be dynamically processed
        later when building the content.

        The result of this method will be a dict containing 'status',
        'script' and 'segments' fields.
        """
        if not hasattr(self, '_for_database'):
            data = ProjectForDatabase(
                ProjectStatus.TO_START.value,
                copy.deepcopy(self.json),
                []
            )

            # TODO: This shortcode parser must be instantiated once, and this is
            # being instantiated twice in this file
            shortcode_parser = ShortcodeParser([YTAShortcodeTag('meme', ShortcodeTagType.SIMPLE)])
            segments = []
            for segment in self.json['segments']:
                segment_data = Segment()

                # TODO: Really? Do I have to do this here (?)
                # fields = [SegmentField.TYPE, SegmentField.URL, SegmentField.TEXT, SegmentField.KEYWORDS, SegmentField.FILENAME, SegmentField.AUDIO_NARRATION_FILENAME, SegmentField.MUSIC, SegmentField.VOICE, SegmentField.ENHANCEMENTS, SegmentField.EXTRA_PARAMS]

                # for field in fields:
                #     setattr(segment_data, field.value, segment[field.value])

                # I fill all the fields and then replace the special
                # ones
                for field in SegmentField.get_all_values():
                    setattr(segment_data, field, segment[field])

                # TODO: What about 'type', 'url', 'text', etc (?)
                # type
                # url
                # text
                # keywords
                # filename
                # audio_narration_filename
                # music
                # voice
                # enhancements (?)
                # extra_params (?)

                # Manually handle 'text_to_narrate'
                if segment.get(SegmentField.TEXT_TO_NARRATE.value, ''):
                    shortcode_parser.parse(segment[SegmentField.TEXT_TO_NARRATE.value])
                    segment_data.text_to_narrate_sanitized_without_shortcodes = shortcode_parser.text_sanitized_without_shortcodes
                    segment_data.text_to_narrate_with_simplified_shortcodes = shortcode_parser.text_sanitized_with_simplified_shortcodes
                    segment_data.text_to_narrate_sanitized = shortcode_parser.text_sanitized

                # Manually handle string duration
                if segment.get(SegmentField.DURATION.value, None) and isinstance(segment[SegmentField.DURATION.value], str):
                    segment_data.duration = StringDuration.convert_duration(segment[SegmentField.DURATION.value])
                        
                segment_data.status = SegmentStatus.TO_START.value
                segment_data.created_at = datetime.now()

                for enhancement in segment.get(SegmentField.ENHANCEMENTS.value, []):
                    enhancement_data = Enhancement()

                    # I fill all the fields and then replace the special
                    # ones
                    for field in EnhancementField.get_all_values():
                        setattr(enhancement_data, field, enhancement[field])

                    # Manually handle 'text_to_narrate'
                    if enhancement.get(EnhancementField.TEXT_TO_NARRATE.value, ''):
                        shortcode_parser.parse(enhancement[EnhancementField.TEXT_TO_NARRATE.value])
                        enhancement_data.text_to_narrate_sanitized_without_shortcodes = shortcode_parser.text_sanitized_without_shortcodes
                        enhancement_data.text_to_narrate_with_simplified_shortcodes = shortcode_parser.text_sanitized_with_simplified_shortcodes
                        enhancement_data.text_to_narrate_sanitized = shortcode_parser.text_sanitized

                    # Manually handle string duration
                    if enhancement.get('duration', None) and PythonValidator.is_string(enhancement['duration']):
                        enhancement_data.duration = StringDuration.convert_duration(enhancement['duration'])

                    enhancement_data.status = SegmentStatus.TO_START.value
                    enhancement_data.created_at = datetime.now()

                    # Store that enhancement data
                    segment_data.enhancements.append(enhancement_data)

                segments.append(segment_data)

            data.segments = segments

            self._for_database = data

        # TODO: This is not being transformed to a dict

        return self._for_database

    @staticmethod
    def validate_project_json(json: dict):
        """
        Validate the project data that is provided as the
        'json' parameter and return it if valid or raises
        an Exception if something is wrong.
        """
        segments = json.get('segments', [])

        if not segments:
            raise Exception('No "segments" in the provided "json" parameter.')

        # TODO: Check main structure and validate fields
        # TODO: This shortcode parser must have the definitive shortcodes
        # TODO: Shortcode handlers must be working, check Notion (https://www.notion.so/ShortcodeParser-personalizado-10ff5a32d462804d9253e9cb782e5540?pvs=4)
        # TODO: This shortcode parser must be instantiated once, and this is
        # being instantiated twice in this file
        shortcode_parser = ShortcodeParser([YTAShortcodeTag('meme', ShortcodeTagType.SIMPLE)])

        for index, segment in enumerate(segments):
            print_in_progress(f'Handling segment [{index}]')

            # Check all fields are set (no more, no less)
            ElementValidator.validate_segment_fields(segment)

            type = ElementParameterValidator.validate_segment_type(segment.get(SegmentField.TYPE.value, '')).value

            # Check if 'text' has any shortcode
            try:
                ShortcodeParser([]).parse(segment.get(SegmentField.TEXT.value, ''))
            except Exception:
                print_error(f'Field "{SegmentField.TEXT.value}" not found or shortcodes found on it.')
                exit()

            # Check if 'text_to_narrate' has unaccepted shortcodes
            try:
                shortcode_parser.parse(segment.get(SegmentField.TEXT_TO_NARRATE.value, ''))
            except Exception:
                print_error(f'Field "{SegmentField.TEXT_TO_NARRATE.value}" not found or unregistered shortcodes found on it.')
                exit()

            # Check 'duration' is valid segment duration string or 
            # positive numeric value
            duration = ElementValidator.validate_segment_duration_field(segment.get(SegmentField.DURATION.value, None))

            # We can only accept 'FILE_DURATION' for some specific types
            ElementValidator.validate_segment_duration_field_for_type(duration, type)

            # TODO: Validate, if premade or effect, that 'extra_data' has
            # needed fields
            keywords = segment.get(SegmentField.KEYWORDS.value, None)
            if type == SegmentType.PREMADE.value:
                # We avoid 'duration' because we obtain it from main fields
                PremadeElementBuilder.extract_extra_params(segment, PremadeElementBuilder.premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'duration'])
            elif type == SegmentType.TEXT.value:
                # We avoid 'text' and 'duration' because we obtain them from main fields
                TextElementBuilder.extract_extra_params(segment, TextElementBuilder.text_premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'output_filename', 'duration', 'text'])
            # TODO: Validate for another types
            
            for index, enhancement in enumerate(segment.get(SegmentField.ENHANCEMENTS.value, [])):
                print_in_progress(f'   Handling enhancement [{index}]')

                # We let the user know if any unaccepted field has been given
                # and we raise and Exception if that happen
                ElementValidator.validate_enhancement_fields(enhancement)

                # Check enhancement type is allowed as enhancement
                enhancement_type = ElementParameterValidator.validate_enhancement_type(enhancement.get(EnhancementField.TYPE.value, '')).value
                
                # Check if 'text' has any shortcode
                try:
                    ShortcodeParser([]).parse(enhancement.get(EnhancementField.TEXT.value, ''))
                except Exception:
                    print_error(f'Field "{EnhancementField.TEXT.value}" not found or shortcodes found on it.')
                    exit()

                # Check if 'text_to_narrate' has unaccepted shortcodes
                try:
                    shortcode_parser.parse(enhancement.get(EnhancementField.TEXT_TO_NARRATE.value, ''))
                except Exception:
                    print_error(f'Field "{EnhancementField.TEXT_TO_NARRATE.value}" not found or unregistered shortcodes found on it.')
                    exit()

                # Check 'duration' is valid enhanacement duration string or
                # positive numeric value
                duration = ElementValidator.validate_enhancement_duration_field(enhancement.get(EnhancementField.DURATION.value, None))
                mode = ElementValidator.validate_enhancement_mode_field(enhancement.get(EnhancementField.MODE.value, None)).value

                # We can only accept 'FILE_DURATION' for some specific types
                ElementValidator.validate_enhancement_duration_field_for_type(duration, enhancement_type)

                # We can only accept the mode if its type accepts it
                ElementValidator.validate_enhancement_mode_field_for_type(mode, enhancement_type)

                RulesChecker.check_need_rules(enhancement, ElementRules.get_subclass_by_type(enhancement_type)())
                # TODO: We should check any other thing that is here (frames, etc.)

                # TODO: Validate, if premade or effect, that 'extra_data' has
                # needed fields
                keywords = enhancement.get(EnhancementField.KEYWORDS.value, None)
                if enhancement_type == EnhancementType.PREMADE.value:
                    # We avoid 'duration' because we obtain it from main fields
                    PremadeElementBuilder.extract_extra_params(enhancement, PremadeElementBuilder.premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'duration'])
                elif enhancement_type == EnhancementType.EFFECT.value:
                    EffectElementBuilder.extract_extra_params(enhancement, EffectElementBuilder.effect_name_to_class(keywords).apply, params_to_ignore = ['self', 'cls', 'args', 'kwargs', 'video'])
                elif enhancement_type == EnhancementType.TEXT.value:
                    # We avoid 'text' and 'duration' because we obtain them from main fields
                    TextElementBuilder.extract_extra_params(enhancement, TextElementBuilder.text_premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'output_filename', 'duration', 'text'])
                # TODO: Validate for another types

            # 4. If doesn't have necessary fields for its type
            RulesChecker.check_need_rules(segment, ElementRules.get_subclass_by_type(type)())

            # TODO: Check main structure and validate fields
            # TODO: This shortcode parser must have the definitive shortcodes
            # TODO: Shortcode handlers must be working, check Notion (https://www.notion.so/ShortcodeParser-personalizado-10ff5a32d462804d9253e9cb782e5540?pvs=4)
            # TODO: This shortcode parser must be instantiated once, and this is
            # being instantiated twice in this file
            shortcode_parser = ShortcodeParser([YTAShortcodeTag('meme', ShortcodeTagType.SIMPLE)])

            for index, segment in enumerate(segments):
                print_in_progress(f'Handling segment [{index}]')

                # Check all fields are set (no more, no less)
                ElementValidator.validate_segment_fields(segment)

                type = ElementParameterValidator.validate_segment_type(segment.get(SegmentField.TYPE.value, '')).value

                # Check if 'text' has any shortcode
                try:
                    ShortcodeParser([]).parse(segment.get(SegmentField.TEXT.value, ''))
                except Exception:
                    print_error(f'Field "{SegmentField.TEXT.value}" not found or shortcodes found on it.')
                    exit()

                # Check if 'text_to_narrate' has unaccepted shortcodes
                try:
                    shortcode_parser.parse(segment.get(SegmentField.TEXT_TO_NARRATE.value, ''))
                except Exception:
                    print_error(f'Field "{SegmentField.TEXT_TO_NARRATE.value}" not found or unregistered shortcodes found on it.')
                    exit()

                # Check 'duration' is valid segment duration string or 
                # positive numeric value
                duration = ElementValidator.validate_segment_duration_field(segment.get(SegmentField.DURATION.value, None))

                # We can only accept 'FILE_DURATION' for some specific types
                ElementValidator.validate_segment_duration_field_for_type(duration, type)

                # TODO: Validate, if premade or effect, that 'extra_data' has
                # needed fields
                keywords = segment.get(SegmentField.KEYWORDS.value, None)
                if type == SegmentType.PREMADE.value:
                    # We avoid 'duration' because we obtain it from main fields
                    PremadeElementBuilder.extract_extra_params(segment, PremadeElementBuilder.premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'duration'])
                elif type == SegmentType.TEXT.value:
                    # We avoid 'text' and 'duration' because we obtain them from main fields
                    TextElementBuilder.extract_extra_params(segment, TextElementBuilder.text_premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'output_filename', 'duration', 'text'])
                # TODO: Validate for another types
                
                for index, enhancement in enumerate(segment.get(SegmentField.ENHANCEMENTS.value, [])):
                    print_in_progress(f'   Handling enhancement [{index}]')

                    # We let the user know if any unaccepted field has been given
                    # and we raise and Exception if that happen
                    ElementValidator.validate_enhancement_fields(enhancement)

                    # Check enhancement type is allowed as enhancement
                    enhancement_type = ElementParameterValidator.validate_enhancement_type(enhancement.get(EnhancementField.TYPE.value, '')).value
                    
                    # Check if 'text' has any shortcode
                    try:
                        ShortcodeParser([]).parse(enhancement.get(EnhancementField.TEXT.value, ''))
                    except Exception:
                        print_error(f'Field "{EnhancementField.TEXT.value}" not found or shortcodes found on it.')
                        exit()

                    # Check if 'text_to_narrate' has unaccepted shortcodes
                    try:
                        shortcode_parser.parse(enhancement.get(EnhancementField.TEXT_TO_NARRATE.value, ''))
                    except Exception:
                        print_error(f'Field "{EnhancementField.TEXT_TO_NARRATE.value}" not found or unregistered shortcodes found on it.')
                        exit()

                    # Check 'duration' is valid enhanacement duration string or
                    # positive numeric value
                        duration = ElementValidator.validate_enhancement_duration_field(enhancement.get(EnhancementField.DURATION.value, None))
                        mode = ElementValidator.validate_enhancement_mode_field(enhancement.get(EnhancementField.MODE.value, None)).value

                        # We can only accept 'FILE_DURATION' for some specific types
                        ElementValidator.validate_enhancement_duration_field_for_type(duration, enhancement_type)

                        # We can only accept the mode if its type accepts it
                        ElementValidator.validate_enhancement_mode_field_for_type(mode, enhancement_type)

                        RulesChecker.check_need_rules(enhancement, ElementRules.get_subclass_by_type(enhancement_type)())
                        # TODO: We should check any other thing that is here (frames, etc.)

                        # TODO: Validate, if premade or effect, that 'extra_data' has
                        # needed fields
                        keywords = enhancement.get(EnhancementField.KEYWORDS.value, None)
                        if enhancement_type == EnhancementType.PREMADE.value:
                            # We avoid 'duration' because we obtain it from main fields
                            PremadeElementBuilder.extract_extra_params(enhancement, PremadeElementBuilder.premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'duration'])
                        elif enhancement_type == EnhancementType.EFFECT.value:
                            EffectElementBuilder.extract_extra_params(enhancement, EffectElementBuilder.effect_name_to_class(keywords).apply, params_to_ignore = ['self', 'cls', 'args', 'kwargs', 'video'])
                        elif enhancement_type == EnhancementType.TEXT.value:
                            # We avoid 'text' and 'duration' because we obtain them from main fields
                            TextElementBuilder.extract_extra_params(enhancement, TextElementBuilder.text_premade_name_to_class(keywords).generate, ['self', 'cls', 'args', 'kwargs', 'output_filename', 'duration', 'text'])
                        # TODO: Validate for another types

                    # 4. If doesn't have necessary fields for its type
                    RulesChecker.check_need_rules(segment, ElementRules.get_subclass_by_type(type)())

            return json
        
@dataclass
class MongoResult:
    """
    @dataclass
    Just a mongo database result obtained from
    a query in the database.
    """

    json: dict
    """
    The json information as it is stored in the
    database.
    """