"""
Silique (https://www.silique.fr)
Copyright (C) 2022-2026

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from typing import Union, List, Iterator, Optional
from itertools import chain
from pathlib import Path

from ruamel.yaml import YAML

from ..convert.path import Paths
from ..error import DictConsistencyError
from ..i18n import _


class Walker:
    def __init__(
        self,
        convert,
    ) -> None:
        """Parse directories content"""
        self.convert = convert
        self.yaml = YAML()
        self.sort_structural_files_all = True
        self.walk()

    def walk(self) -> None:
        rougailconfig = self.convert.rougailconfig
        self.sort_structural_files_all = rougailconfig["sort_structural_files_all"]
        if rougailconfig["main_namespace"]:
            self.load_with_extra(
                rougailconfig["extra_namespaces"],
                rougailconfig["main_namespace"],
                rougailconfig["main_structural_directories"],
                rougailconfig["isolated_namespace"],
            )
        else:
            self.convert.namespace = None
            for filename in self.get_sorted_filenames(
                rougailconfig["main_structural_directories"]
            ):
                self.parse_variable_file(
                    filename,
                )

    def load_with_extra(
        self,
        extra_structures: dict,
        main_namespace: Optional[str] = None,
        main_structures: Optional[List[str]] = None,
        isolated_namespace: bool = True,
    ) -> None:
        directory_dict = chain(
            (
                (
                    main_namespace,
                    main_structures,
                ),
            ),
            extra_structures.items(),
        )
        for namespace, directories in directory_dict:
            self.convert.create_namespace(namespace, isolated_namespace)
            for filename in self.get_sorted_filenames(directories):
                self.parse_variable_file(
                    filename,
                )

    def get_sorted_filenames(
        self,
        directories: Union[str, List[str]],
    ) -> Iterator[str]:
        """Sort filename"""
        if not isinstance(directories, list):
            directories = [directories]
        if self.sort_structural_files_all:
            filenames = {}
        for directory_name in directories:
            directory = Path(directory_name)
            if not self.sort_structural_files_all:
                filenames = {}
            if directory.is_file():
                self.get_filename(directory, filenames)
            else:
                for file_path in directory.iterdir():
                    self.get_filename(file_path, filenames)
            if not self.sort_structural_files_all:
                yield from self._get_sorted_filenames(filenames)
        if self.sort_structural_files_all:
            yield from self._get_sorted_filenames(filenames)

    def _get_sorted_filenames(self, filenames):
        for filename in sorted(filenames):
            yield filenames[filename]

    def get_filename(self, file_path, filenames: List[str]) -> None:
        if file_path.suffix not in [".yml", ".yaml"]:
            return
        if file_path.name in filenames:
            raise DictConsistencyError(
                _("duplicate structural file name {0}").format(file_path.name),
                78,
                [filenames[file_path.name], str(file_path)],
            )
        filenames[file_path.name] = str(file_path)

    def parse_variable_file(
        self,
        filename: str,
    ) -> None:
        """Parse file"""
        with open(filename, encoding="utf8") as file_fh:
            objects = self.yaml.load(file_fh)
        if objects is None:
            return
        version = self.convert.validate_file_version(
            objects,
            filename,
        )
        self.convert.parse_root_file(
            [filename],
            self.convert.namespace,
            version,
            objects,
        )


__all__ = ("Walker",)
