import logging
from pathlib import Path
from typing import Any, List, Tuple

import yaml


# loading package files: https://stackoverflow.com/a/20885799
try:
    import importlib.resources as pkg_resources
except ImportError:
    # Try backported to PY<37 `importlib_resources`.
    import importlib_resources as pkg_resources

from . import resources


lowerCAmelCase__					=				'https://github.com/huggingface/datasets/tree/main/src/datasets/utils'
lowerCAmelCase__					=				F'{BASE_REF_URL}/{__file__}'
lowerCAmelCase__					=				logging.getLogger(__name__)






def    __lowerCamelCase	(		lowerCAmelCase__			):
       lowerCAmelCase__  			=      pkg_resources.read_text(lowerCAmelCase__			,						lowerCAmelCase__			)
       return yaml.safe_load(lowerCAmelCase__			), F"""{BASE_REF_URL}/resources/{resource}"""


lowerCAmelCase__		,	lowerCAmelCase__					=				load_yaml_resource('readme_structure.yaml')

lowerCAmelCase__					=				[
    '[Needs More Information]',
    '[More Information Needed]',
    '(https://github.com/huggingface/datasets/blob/main/CONTRIBUTING.md#how-to-contribute-to-the-dataset-cards)',
]

# Dictionary representation of section/readme, error_list, warning_list
lowerCAmelCase__					=				Tuple[dict, List[str], List[str]]
class        a_				:






 '''simple docstring'''






 def __init__(    self      :						Union[str, Any]    ,					lowercase__      :						str    ,					lowercase__      :						str    ,					lowercase__      :						List[str] = None    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        lowerCAmelCase__  			=      name
        lowerCAmelCase__  			=      level
        lowerCAmelCase__  			=      lines
        lowerCAmelCase__  			=      ''
        lowerCAmelCase__  			=      True
        lowerCAmelCase__  			=      {}
        lowerCAmelCase__  			=      []
        lowerCAmelCase__  			=      []
        if self.lines is not None:
               self.parse(suppress_parsing_errors=lowercase__)




 def 	__snake_case		(    self      :						Tuple    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        lowerCAmelCase__  			=      ''
        lowerCAmelCase__  			=      []
        lowerCAmelCase__  			=      False
        for line in self.lines:
               if line.strip(' \n') == "":
                      continue
               elif line.strip(' \n')[:3] == "```":
                      lowerCAmelCase__  			=      not code_start
               elif line.split()[0] == self.level + "#" and not code_start:
                      if current_sub_level != "":
                             lowerCAmelCase__  			=      Section(lowercase__    ,					self.level + '#'    ,					lowercase__)
                             lowerCAmelCase__  			=      []
                      else:
                             if current_lines != []:
                                    self.text += "".join(lowercase__).strip()
                                    if self.text != "" and self.text not in FILLER_TEXT:
                                           lowerCAmelCase__  			=      False
                                    lowerCAmelCase__  			=      []

                      lowerCAmelCase__  			=      ' '.join(line.split()[1:]).strip(' \n')
               else:
                      current_lines.append(lowercase__)
        else:
               if current_sub_level != "":
                      if current_sub_level in self.content:
                             self.parsing_error_list.append(
                                 F"""Multiple sections with the same heading `{current_sub_level}` have been found. Please keep only one of these sections.""")
                      lowerCAmelCase__  			=      Section(lowercase__    ,					self.level + '#'    ,					lowercase__)
               else:
                      if current_lines != []:
                             self.text += "".join(lowercase__).strip()
                             if self.text != "" and self.text not in FILLER_TEXT:
                                    lowerCAmelCase__  			=      False

        if self.level == "" and not suppress_parsing_errors:
               if self.parsing_error_list != [] or self.parsing_warning_list != []:
                      lowerCAmelCase__  			=      lowerCAmelCase__  			=      '\n'.join('-\t' + x for x in self.parsing_error_list + self.parsing_warning_list)
                      lowerCAmelCase__  			=      F"""The following issues were found while parsing the README at `{self.name}`:\n""" + errors
                      raise ValueError(lowercase__)




 def 	__snake_case		(    self      :						List[str]    ,					lowercase__      :						dict):







        '''simple docstring'''



        lowerCAmelCase__  			=      []
        lowerCAmelCase__  			=      []
        if structure["allow_empty"] is False:
               # If content is expected
               if self.is_empty_text and self.content == {}:
                      # If no content is found, mention it in the error_list
                      error_list.append(F"""Expected some content in section `{self.name}` but it is empty.""")

        if structure["allow_empty_text"] is False:
               # If some text is expected
               if self.is_empty_text:
                      # If no text is found, mention it in the error_list
                      error_list.append(
                          F"""Expected some text in section `{self.name}` but it is empty (text in subsections are ignored).""")
        # Subsections Validation
        if structure["subsections"] is not None:
               # If subsections are expected
               if self.content == {}:
                      # If no subsections are present
                      lowerCAmelCase__  			=      [subsection['name'] for subsection in structure['subsections']]
                      # Mention the expected values in the error_list
                      error_list.append(
                          F"""Section `{self.name}` expected the following subsections: {', '.join(['`'+x+'`' for x in values])}. Found 'None'.""")
               else:
                      # If some subsections are present
                      lowerCAmelCase__  			=      [subsection['name'] for subsection in structure['subsections']]
                      lowerCAmelCase__  			=      False
                      for idx, name in enumerate(lowercase__):
                             if name not in self.content:
                                    # If the expected subsection is not present
                                    error_list.append(F"""Section `{self.name}` is missing subsection: `{name}`.""")
                                    lowerCAmelCase__  			=      True
                             else:
                                    # If the subsection is present, validate subsection, return the result
                                    # and concat the errors from subsection to section error_list

                                    # Skip sublevel validation if current level is `###`
                                    if self.level == "###":
                                           continue
                                    else:
                                           lowerCAmelCase__			,      lowerCAmelCase__			,      lowerCAmelCase__  			=      self.content[name].validate(
                                               structure['subsections'][idx])
                                    error_list += subsec_error_list
                                    warning_list += subsec_warning_list

                      if has_missing_subsections:  # we only allow to have extra subsections if all the other ones are here
                             for name in self.content:
                                    if name not in structure_names:
                                           # If an extra subsection is present
                                           warning_list.append(
                                               F"""`{self.name}` has an extra subsection: `{name}`. Skipping further validation checks for this subsection as expected structure is unknown.""")
        if error_list:
               # If there are errors, do not return the dictionary as it is invalid
               return {}, error_list, warning_list
        else:
               return self.to_dict(), error_list, warning_list




 def 	__snake_case		(    self      :						List[Any]):







        '''simple docstring'''



        return {
            "name": self.name,
            "text": self.text,
            "is_empty_text": self.is_empty_text,
            "subsections": [value.to_dict() for value in self.content.values()],
        }






class        a_				(		SCREAMING_SNAKE_CASE		):  # Level 0






 '''simple docstring'''






 def __init__(    self      :						Any    ,					lowercase__      :						str    ,					lowercase__      :						List[str]    ,					lowercase__      :						dict = None    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        super().__init__(name=lowercase__    ,					level='')  # Not using lines here as we need to use a child class parse
        lowerCAmelCase__  			=      structure
        lowerCAmelCase__  			=      -2
        lowerCAmelCase__  			=      0
        lowerCAmelCase__  			=      lines
        if self.lines is not None:
               self.parse(suppress_parsing_errors=lowercase__)




 def 	__snake_case		(    self      :						Any):







        '''simple docstring'''



        if self.structure is None:
               lowerCAmelCase__			,      lowerCAmelCase__			,      lowerCAmelCase__  			=      self._validate(lowercase__)
        else:
               lowerCAmelCase__			,      lowerCAmelCase__			,      lowerCAmelCase__  			=      self._validate(self.structure)
        if error_list != [] or warning_list != []:
               lowerCAmelCase__  			=      '\n'.join(['-\t' + x for x in error_list + warning_list])
               lowerCAmelCase__  			=      F"""The following issues were found for the README at `{self.name}`:\n""" + errors
               raise ValueError(lowercase__)




 @classmethod
 def 	__snake_case		(    cls      :						List[str]    ,					lowercase__      :						Path    ,					lowercase__      :						dict = None    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        with open(lowercase__    ,					encoding='utf-8') as f:
               lowerCAmelCase__  			=      f.readlines()
        return cls(lowercase__    ,					lowercase__    ,					lowercase__    ,					suppress_parsing_errors=lowercase__)




 @classmethod
 def 	__snake_case		(    cls      :						Any    ,					lowercase__      :						str    ,					lowercase__      :						dict = None    ,					lowercase__      :						str = "root"    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        lowerCAmelCase__  			=      string.split('\n')
        return cls(lowercase__    ,					lowercase__    ,					lowercase__    ,					suppress_parsing_errors=lowercase__)




 def 	__snake_case		(    self      :						Optional[int]    ,					lowercase__      :						bool = False):







        '''simple docstring'''



        lowerCAmelCase__  			=      0

        for line in self.lines:
               self.yaml_tags_line_count += 1
               if line.strip(' \n') == "---":
                      self.tag_count += 1
                      if self.tag_count == 2:
                             break
               line_count += 1
        if self.tag_count == 2:
               lowerCAmelCase__  			=      self.lines[line_count + 1 :]  # Get the last + 1 th item.
        else:
               lowerCAmelCase__  			=      self.lines[self.tag_count :]
        super().parse(suppress_parsing_errors=lowercase__)




 def __str__(    self      :						Optional[int]):







        '''simple docstring'''



        return str(self.to_dict())




 def 	__snake_case		(    self      :						int    ,					lowercase__      :						Dict):







        '''simple docstring'''



        lowerCAmelCase__  			=      []
        lowerCAmelCase__  			=      []
        if self.yaml_tags_line_count == 0:
               warning_list.append('Empty YAML markers are present in the README.')
        elif self.tag_count == 0:
               warning_list.append('No YAML markers are present in the README.')
        elif self.tag_count == 1:
               warning_list.append('Only the start of YAML tags present in the README.')
        # Check how many first level sections are present.
        lowerCAmelCase__  			=      len(self.content.keys())
        if num_first_level_keys > 1:
               # If more than one, add to the error list, continue
               error_list.append(
                   F"""The README has several first-level headings: {', '.join(['`'+x+'`' for x in list(self.content.keys())])}. Only one heading is expected. Skipping further validation for this README.""")
        elif num_first_level_keys < 1:
               # If less than one, append error.
               error_list.append(
                   'The README has no first-level headings. One heading is expected. Skipping further validation for this README.')

        else:
               # If one exactly
               lowerCAmelCase__  			=      list(self.content.keys())[0]  # Get the key
               if start_key.startswith('Dataset Card for'):  # Check correct start
                      # If the starting is correct, validate all the sections
                      lowerCAmelCase__			,      lowerCAmelCase__			,      lowerCAmelCase__  			=      self.content[start_key].validate(
                          readme_structure['subsections'][0])
                      error_list += sec_error_list
                      warning_list += sec_warning_list
               else:
                      # If not found, append error
                      error_list.append(
                          'No first-level heading starting with `Dataset Card for` found in README. Skipping further validation for this README.')
        if error_list:
               # If there are errors, do not return the dictionary as it is invalid
               return {}, error_list, warning_list
        else:
               return self.to_dict(), error_list, warning_list


if __name__ == "__main__":
    from argparse import ArgumentParser

    lowerCAmelCase__					=				ArgumentParser(usage='Validate the content (excluding YAML tags) of a README.md file.')
    ap.add_argument('readme_filepath')
    lowerCAmelCase__					=				ap.parse_args()
    lowerCAmelCase__					=				Path(args.readme_filepath)
    lowerCAmelCase__					=				ReadMe.from_readme(readme_filepath)

def    __lowerCamelCase	(		lowerCAmelCase__			):
       return " ".join([word[::-1] for word in input_str.split()]			)


if __name__ == "__main__":
    import doctest

    doctest.testmod()
